题目链接 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);
}