树形DP,没有上司的舞会的模型
根据题目可以知道, 每一个联通块里有且只有一个环, 所以我们找到这个环然后从中间把它断开, 对断开的两个端点u1, u2, 分别舞会。
设dp[u][0]为不选u, dp[u][1]为选u,
那么这个联通块答案就是max(dp[u1][0], dp[u2][0])。
注意有好多联通块。对于所有联通块跑dp,加起来就是答案
我在这里解释一下为什么是max(dp[u1][0],dp[u2][0])
就拿样例来举例:
3
10 2
20 3
30 1
如果把2和3断开,那么就形成一条链2-1-3,如果是取了dp[u1][1]或dp[u2][1]的话,答案就会变成50,因为2和3都取了,但事实上不行,所以只能是max(dp[u1][0],dp[u2][0])
#include<bits/stdc++.h>
using namespace std;
const int N=1000002;
typedef long long ll;
struct node{
int to,ne;
}e[N<<1];
ll ans,s,f[N][2];
int vis[N],n,x,y,h[N],w[N],tot=1,k,i;
inline char gc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
#define gc getchar
inline int read(){
int x=0,fl=1;char ch=gc();
for (;ch<48||ch>57;ch=gc())if(ch=='-')fl=-1;
for (;48<=ch&&ch<=57;ch=gc())x=(x<<3)+(x<<1)+(ch^48);
return x*fl;
}
void add(int x,int y){
e[++tot]=(node){y,h[x]};
h[x]=tot;
}
void find(int u,int pre){
vis[u]=1;
for (int i=h[u],v;i;i=e[i].ne)
if ((i^1)!=pre){
if (vis[v=e[i].to]){
x=u;y=v;k=i;
continue;
}
find(v,i);
}
}
void dfs(int u,int pre){
f[u][0]=0;f[u][1]=w[u];
for (int i=h[u],v;i;i=e[i].ne)
if (i!=k && (i^1)!=k && (i^1)!=pre){
dfs(v=e[i].to,i);
f[u][1]+=f[v][0];
f[u][0]+=max(f[v][0],f[v][1]);
}
}
int main(){
n=read();
for (i=1;i<=n;i++) w[i]=read(),x=read(),add(i,x),add(x,i);
for (i=1;i<=n;i++)
if (!vis[i]){
find(i,-1);
dfs(x,-1);
s=f[x][0];
dfs(y,-1);
ans+=max(s,f[y][0]);
}
printf("%lld",ans);
}