题目直接链接
题意:
中文题,也挺明白的,就不过多的叙述。
分析:
首先我们把条件转换一下,其实a憎恨b和b憎恨a的效果是等价的,说白了就是a和b不选在一起,然后就是考虑一下,我们先建一个图,然后找一下连通分量(当然是无向图了,刚才已经说了,是等价的)每个联通分量的结果是不会互相影响的,因为没有相互憎恨的关系,那这样是不是想到了一道题:没有上司的舞会。类似的,但是这个是图,那个是树。
而在图上dp就不是很好了,但是我们可以考虑一下这个图的特殊性质:如果某个分量(边去了重)里有x个点,那么他只可能有x-1个边或者x个边,因为少于x-1个边将不会联通,而每个点又最多为次分量提供一条边(如果重了就是不再提供),当然如果是x-1多条边那么就非常简单了,那就直接树形dp就好了。
我们来考虑一下如有x个边怎么样,如果有x个边的我们可以把他转换成x-1条边的树,怎么转换呢,我们可以这样想,x边的联通图去掉1条特定的边,那么他一定能成为一棵树,我们只要找到可以的一条边然后两次Dfs进行dp,第一次不取这条边连接的某个点,第二次不取另一个点然后取max就好了,这条边怎么找到呢,其实找到环上的任何一条边都可以。而找环就可应直接读边的时候处理出来就好了,最后就是代码。
#include <cstdio> #include <string> using namespace std; const int maxn=1000000+10; long long w[maxn]; int e[maxn]; int f[maxn]; int x1[maxn]; int x2[maxn]; int t[maxn]; long long Dp[maxn][2]; bool vis[maxn]; //并查集 void Init(int n){ for(int i=1;i<=n;i++) f[i]=i; } int Find(int a){ return a==f[a]?a:(f[a]=Find(f[a])); } void Co(int a,int b){ int fa=Find(a); int fb=Find(b); f[fb]=fa; } //边表 int head[maxn]; struct E{ int to; int next; E(){ to=next=0; } }ed[maxn*2]; int tot; void J(int a,int b){ tot++; ed[tot].to=b; ed[tot].next=head[a]; head[a]=tot; } void Dfs(int x,int fa,int ix,int id){ vis[x]=1;//只是供后面用 Dp[x][0]=0; if(x==ix)//不能选x Dp[x][1]=0; else Dp[x][1]=w[x]; for(int i=head[x];i;i=ed[i].next){ if(ed[i].to==fa||i==id||i==((id%2)?(id+1):(id-1))) continue; Dfs(ed[i].to,x,ix,id); Dp[x][0]+=max(Dp[ed[i].to][1],Dp[ed[i].to][0]); Dp[x][1]+=Dp[ed[i].to][0]; } } int main(){ int n; int gs=0; scanf("%d",&n); Init(n); for(int i=1;i<=n;i++){ scanf("%lld%d",&w[i],&t[i]); if(t[t[i]]!=i){//重边不再加 J(i,t[i]); J(t[i],i); } else continue; if(Find(i)!=Find(t[i])) Co(i,t[i]); else{ gs++;//处理出需要的边 x1[gs]=i; x2[gs]=t[i]; e[gs]=tot; } } long long ans=0; for(int i=1;i<=gs;i++){//x边的分量的处理 Dfs(x1[i],0,x2[i],e[i]); long long js1=max(Dp[x1[i]][1],Dp[x1[i]][0]); Dfs(x2[i],0,x1[i],e[i]); long long js2=max(Dp[x2[i]][1],Dp[x2[i]][0]); ans+=max(js1,js2); } for(int i=1;i<=n;i++)//(x-1)边的分量的处理 if(!vis[i]){ Dfs(i,0,0,0); ans+=max(Dp[i][0],Dp[i][1]); } printf("%lld",ans); return 0; }