做法:类似树形DP,但细节较多。
注意点:1.树形DP回溯时dp[u][1]+=dp[v][0],而不是取最小值;
2.dfs的时候要全部搜到,不能一搜到重复的点就直接return了。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
using namespace std;
typedef long long LL;
const int maxn=1000010;
vector<int>g[maxn];
int val[maxn];
int n;
LL dp[maxn][2];
int vis[maxn];
int fa[maxn];
void read(int &x){
x=0;
char c=getchar();
while(c<'0'|| x>'9') c=getchar();
while(c>='0' && c<='9'){
x=x*10+c-'0';
c=getchar();
}
}
int A,B;
void dfs(int u){
//printf("u=%d ",u);
for(int i=0;i<g[u].size();i++){
int v=g[u][i];
//printf("v=%d\n",v);
if(v==fa[u] || fa[v]==u) continue;
if(vis[v]){
A=u,B=v;
return;
}
vis[v]=1;
fa[v]=u;
dfs(v);
}
}
void DP(int u,int lim,int cnt){
dp[u][0]=0;
dp[u][1]=val[u];
for(int i=0;i<g[u].size();i++){
int v=g[u][i];
if(vis[v]>cnt || v==fa[u] || (u==A && v==B) || (u==B && v==A)) continue;
vis[v]++;
DP(v,lim,cnt);
dp[u][0]+=max(dp[v][0],dp[v][1]);
dp[u][1]+=dp[v][0];
}
if(u==lim) dp[u][1]=dp[u][0];
}
int pre[maxn];
int main(){
scanf("%d",&n);
int x;
for(int i=1;i<=n;i++){
read(val[i]);
read(x);
g[i].push_back(x);
g[x].push_back(i);
}
LL ans1,ans2;
LL ans=0;
for(int i=1;i<=n;i++){
if(!vis[i]){
A=0;B=0;
vis[i]=1;
dfs(i);
vis[i]++;
DP(i,A,1);
ans1=max(dp[i][0],dp[i][1]);
vis[i]++;
DP(i,B,2);
ans2=max(dp[i][0],dp[i][1]);
ans+=max(ans1,ans2);
}
}
printf("%lld",ans);
return 0;
}
^_^