bzoj1040: [ZJOI2008]骑士
题目链接
题解
讨厌关系出度为0,可能有环,并且每个联通块内只有一个环 没有自环
构成基环树森林
选取骑士不能去相邻的
考虑将一个联通块中的环断开,这样就构成一棵树,对于断开的边相连的点分别不取,进行dp
答案就是每个联通块的贡献和
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#define int long long
const int maxn = 1000007;
inline int read () {
int x = 0,f = 1;
char c = getchar ();
while(c < '0' || c > '9'){if(c == '-') f = -1; c = getchar();}
while(c <= '9' && c >= '0') x = x * 10 + c - '0',c = getchar();
return x*f;
}
struct node{
int v,next;
}edge[maxn << 1];
int head[maxn],num = 1;
bool vis[maxn];
inline void Add_edge(int u,int v) { edge[++ num].v = v,edge[num].next = head[u];head[u] = num; }
int son [maxn] ,Val [maxn];
int n,father[maxn],R1,R2,Flag,val;
void Find_Link(int x,int fa) {
vis[x] = 1;
for(int i = head[x];i;i = edge[i].next) {
int v = edge[i].v;
if(v == fa) continue;
if(vis[v]) {
R1 = x;R2 = v;Flag = i;
}
else Find_Link(v,x);
}
}
int dp[maxn][2];
void dfs(int x,int fa) {
dp[x][1] += Val[x];
for(int i = head[x];i;i = edge[i].next) {
int v = edge[i].v;
if(v == fa || i == Flag || (i^1) == Flag) continue;
dfs(v,x);
dp[x][1] += dp[v][0];
dp[x][0] += std::max(dp[v][1],dp[v][0]);
}
}
main () {
n = read();
for(int a,i = 1;i <= n;++ i) {
Val[i] = read();
Add_edge(a = read(),i);
Add_edge(i,a);
}
int ans = 0 ;
for(int tmp,i = 1;i <= n;++ i) {
if(!vis[i]) {
R1 = R2 = 0;
Find_Link(i,i);
if(R1 == R2) {
dfs(i,i);
ans += std::max(dp[i][0],dp[i][1]);
continue;
}
dfs(R1,R1);tmp = dp[R1][0];
memset(dp,0,sizeof dp);
dfs(R2,R2);ans += std::max(tmp,dp[R2][0]);
}
}
printf("%lld\n",ans);
// return 0;
}