[BZOJ 1040] 骑士(基环森林DP)
由题目可知,每个点的出度都为1,则只可能是树或者是基环内向树,但是全图不一定联通,所以可能是基环森林,那么我们对于每一颗树,用dfs找环,找到环的入口处的两个端点即可做两次树形DP,取较大值加到总答案中即可。
以下是代码:
#include<bits/stdc++.h>
#define maxn 1000007
#define out(x) printf("%d",x)
#define in(x) scanf("%d",&x)
using namespace std;
int n,cnt=1,val[maxn],head[maxn];
long long f[maxn][2];
bool vis[maxn];
struct edge{
int nxt;
int to;
}e[maxn<<1];
void add(int u,int v){
e[++cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
}
int r1,r2,nops;
void dfs(int u,int fa){
vis[u]=1;
for(int v,i=head[u];i;i=e[i].nxt){
v=e[i].to;
if(v==fa) continue;
if(!vis[v]) dfs(v,u);
else {
r1=u;r2=v;
nops=i;
}
}
}
void dp(int u,int fa){
f[u][0]=0;f[u][1]=val[u];
for(int v,i=head[u];i;i=e[i].nxt){
v=e[i].to;
if(v==fa) continue;
if(i==nops||i==(nops^1)) continue;
dp(v,u);
f[u][1]+=f[v][0];
f[u][0]+=max(f[v][0],f[v][1]);
}
}
int main(){
in(n);
for(int i=1,v;i<=n;i++){
in(val[i]);in(v);
add(i,v);add(v,i);
}
long long tp,ans=0;
for(int i=1;i<=n;i++){
if(vis[i]) continue;
dfs(i,-1);
dp(r1,-1);
tp=f[r1][0];
dp(r2,-1);
ans+=max(tp,f[r2][0]);
}
printf("%lld",ans);
return 0;
}