ZJOI 骑士

题目链接 https://www.luogu.com.cn/problem/P2607

分析

这道题看了的确很眼熟,加一个0/1状态就可以进行转移了,后来才知道是没有上司的舞会那道题的思路。
打完一个树形DP后感觉有些问题—————它一开始的状态可能不是一棵树啊,的确如果是树的话,和没有上司的舞会是一个类型,那它不是树,又是什么呢?百度后得知,这其实是个基环树,什么是基环树?
基环树其实就是类似树的东西上边套了一个环,或者说环套在树上,看起来挺高端的但这个题用不到太高端的知识,只需要用到它的一个性质,在环上去掉一条边就会成为一棵树,求环的话可以用topsort也可以dfs,由于我看了半天topsort也不会实现,所以写了dfs。
接下来要做的事情就是找到环上的一条边,并且把它断开,以两个端点分别为根节点跑树形dp,这里有一个小技巧,比如两端分别为(u)(v),以(u)为根节点时,默认(u)不选,为什么呢?因为如果(u)选了,那(v)一定不能选,这就增大了我们在dp时的判断难度,所以默认它不选,那如果要选它呢?显然就在以(v)为根节点dp的时候考虑了。
别的其实跟没有上司的舞会简直一模一样,不再赘述。

#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int N=1e6+100;
struct Edge{
    int to,nxt;
}e[N<<1];
int val[N],Head[N],len=1;
void Ins(int a,int b){
    e[++len].to=b;e[len].nxt=Head[a];Head[a]=len;
}
int u,v,E;bool vis[N];
ll dp[N][2];
void find(int x,int fa){
    vis[x]=1;
    for(int i=Head[x];i;i=e[i].nxt){
        if((i^1)==fa)continue;
        int to=e[i].to;
        if(vis[to]){
            u=x,v=to;
            E=i;continue;
        }
        find(to,i);
    }
}
void dfs(int x,int fa){
    dp[x][0]=0;
    dp[x][1]=val[x];
    for(int i=Head[x];i;i=e[i].nxt){
        if((i^1)==fa||i==E||(i^1)==E)continue;
        int to=e[i].to;
        dfs(to,i);
        dp[x][1]+=dp[to][0];
        dp[x][0]+=max(dp[to][1],dp[to][0]);
    }
}
int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        int b;
        scanf("%d%d",&val[i],&b);
        Ins(i,b);Ins(b,i);
    }
    ll ans=0;
    for(int i=1;i<=n;i++){
        if(vis[i])continue;
        find(i,-1);
        dfs(u,-1);
        ll tep=dp[u][0];
        dfs(v,-1);
        ans+=max(tep,dp[v][0]);
    }
    printf("%lld
",ans);
}
原文地址:https://www.cnblogs.com/anyixing-fly/p/12762820.html