基环树上的“没有上司的舞会”。
很显然,最终的图是若干个联通块,那么在读入的时候用并查集进行维护,如果两个点不在同一个联通块内就建一条双向边,如果在同一个联通块内,那么就记录该联通块的两端点的此时两点。
把每个联通块分开来做,对于没个联通块的两个端点均做一次dp,注意,最后的答案统计应该是max(f[num[i].x][0],f[num[i].y][0])。
为什么端点不选?在做dp过程中,我们不知道和端点相连的那个另一个端点是否被选了,即使知道的话,由于要维护dp的正确性,也不好控制不选,所以就强制了端点不选。
两个端点,不可能同时选择,所以这样做是合法的,进行两次操作,每次强制一个端点不选,那么另一个端点的选与不选,就完全依靠dp了。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+5;
int n,v,tot,now,ans,xx,yy;
int c[N],f[N],dp[N][2];
int cnt,head[N];
struct edge{int next,to;}e[N<<1];
struct node{int x,y;}num[N];
int find(int x)
{
if (f[x]==x) return x;
return f[x]=find(f[x]);
}
inline void add(int u,int v)
{
cnt++;
e[cnt].next=head[u];
e[cnt].to=v;
head[u]=cnt;
}
void dfs(int u,int fa)
{
dp[u][0]=dp[u][1]=0;
dp[u][1]=c[u];
for (register int i=head[u]; i; i=e[i].next)
if (e[i].to!=fa)
{
dfs(e[i].to,u);
dp[u][0]+=max(dp[e[i].to][0],dp[e[i].to][1]);
dp[u][1]+=dp[e[i].to][0];
}
}
signed main(){
scanf("%lld",&n);
for (register int i=1; i<=n; ++i) f[i]=i;
for (register int i=1; i<=n; ++i)
{
scanf("%lld%lld",&c[i],&v);
xx=find(i); yy=find(v);
if (xx!=yy)
{
f[xx]=yy;
add(i,v); add(v,i);
}
else tot++,num[tot].x=i,num[tot].y=v;
}
for (register int i=1; i<=tot; ++i)
{
now=0;
dfs(num[i].x,0);
now=max(now,dp[num[i].x][0]);
dfs(num[i].y,0);
now=max(now,dp[num[i].y][0]);
ans+=now;
}
printf("%lld\n",ans);
return 0;
}
来源:CSDN
作者:Dove_xyh
链接:https://blog.csdn.net/Dove_xyh/article/details/103866800