洛谷P2607 [ZJOI2008]骑士
13 April 2019 |
duanyll | Tags:
OI
题解
DP
经典题,n个点n条边的图一定是一个基环树森林,于是可以先DFS一遍把环找出来,再断环(在环上随便找一条边,强制两个端点之一不选),就变成了一个没有上司的舞会了。
\[dp[u][0] = \sum_{v}max(dp[v][0],dp[v][1])\] \[dp[u][1] = \sum_{v}dp[v][0]\]#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
using namespace std;
typedef long long int64;
const int MAXN = 1e6+10;
const int INF = 0x3f3f3f3f;
class lfs {
public:
int w[MAXN];
lfs(int N) {
memset(head, -1, sizeof head);
memset(e,0,sizeof e);
ecnt = 0;
n = N;
}
void adde(int from, int to) {
e[ecnt].to = to;
e[ecnt].next = head[from];
head[from] = ecnt++;
}
void addde(int a, int b) {
adde(a, b);
adde(b, a);
}
int64 solve(){
memset(vis,false,sizeof vis);
memset(dp,0,sizeof dp);
int64 ans = 0;
for(int i = 1;i<=n;i++){
if(!vis[i]){
dfs1(i,0);
dfs2(e[edge_to_break].to,0);
int64 now = dp[e[edge_to_break].to][0];
dfs2(e[edge_to_break^1].to,0);
now = max(now,dp[e[edge_to_break^1].to][0]);
ans += now;
}
}
return ans;
}
protected:
struct Edge {
int to, next;
} e[MAXN * 2];
int head[MAXN];
int ecnt;
int n;
private:
bool vis[MAXN];
int64 dp[MAXN][2];
int edge_to_break;
void dfs1(int u,int fa) {
vis[u] = true;
for (int i = head[u]; i != -1; i = e[i].next) {
int v = e[i].to;
if (v != fa) {
if(vis[v]){
edge_to_break = i;
continue; // <============!!!
}
dfs1(v,u);
}
}
}
void dfs2(int u, int fa) {
dp[u][0] = 0;
dp[u][1] = w[u];
for (int i = head[u]; i != -1; i = e[i].next) {
int v = e[i].to;
if (v != fa && i != edge_to_break && (i^1) != edge_to_break) {
dfs2(v, u);
dp[u][0] += max(dp[v][0],dp[v][1]);
dp[u][1] += dp[v][0];
}
}
// clog << "dp[" << u << "][0] = " << dp[u][0] << endl;
// clog << "dp[" << u << "][1] = " << dp[u][1] << endl;
}
};
#include <cctype>
#include <cstdio>
inline int read() {
int X = 0, w = 0;
char ch = 0;
while (!isdigit(ch)) {
w |= ch == '-';
ch = getchar();
}
while (isdigit(ch)) {
X = (X << 3) + (X << 1) + (ch ^ 48);
ch = getchar();
}
return w ? -X : X;
}
int main(){
// freopen("data","r",stdin);
int n = read();
lfs* tree = new lfs(n);
for(int i = 1;i<=n;i++){
tree->w[i] = read();
tree->addde(i,read());
}
cout << tree->solve() << endl;
}
有一个细节问题是标记处的continue
,如果改为return
就会引起无限递归爆栈,不知道为什么。