骑士,题解

题目直接链接

题意:

  中文题,也挺明白的,就不过多的叙述。

分析:

  首先我们把条件转换一下,其实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;
}
原文地址:https://www.cnblogs.com/wish-all-ac/p/12760082.html