ZJOI2008 骑士

题目连接:戳我

基环树上树形DP。

强行断环边。。对于一个点u,断(u,to[u])边之后,是以to[u]为根的树形DP,如何处理不选这条边呢?当处理到u的时候,将dp[to[u]][1]设置成-0x3f3f3f3f即可。断了(fa[u],u)之后,是以u为根的树形DP,之后操作同上。

备注:(dp[i][1])表示以i为根的子树的最大收获,选择i。(dp[i][0])则表示不选。

状态转移感觉不必多说了qwq。。。。。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 1000010
using namespace std;
int n,m,now,t;
int to[MAXN],head[MAXN],sum[MAXN],in[MAXN];
bool done[MAXN],vis[MAXN];
long long ans;
long long dp[MAXN][2];
struct Edge{int nxt,to;}edge[MAXN];
inline void add(int from,int to){edge[++t].nxt=head[from],edge[t].to=to,head[from]=t;}
inline void search(int x)
{
	done[x]=1;
	int v=to[x];
	if(done[v]) now=x;
	else search(v);
}
inline void solve(int x)	
{
	vis[x]=1,dp[x][0]=0,dp[x][1]=sum[x];
	for(int i=head[x];i;i=edge[i].nxt)
	{
		int v=edge[i].to;
		if(v==now) dp[v][1]=-0x3f3f3f3f;
		else
		{
			solve(v);
			dp[x][0]+=max(0*1ll,max(dp[v][1],dp[v][0]));
			dp[x][1]+=max(0*1ll,dp[v][0]);
		}
	}
}
int main()
{
	#ifndef ONLINE_JUDGE
	freopen("ce.in","r",stdin);
	#endif
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d%d",&sum[i],&to[i]),add(to[i],i);
	for(int i=1;i<=n;i++)
	{
		long long cur_ans=-0x7f7f7f7f;
		if(vis[i]) continue;
		search(i);
		solve(now);
		cur_ans=max(cur_ans,max(dp[now][0],dp[now][1]));
		now=to[now];
		solve(now);
		cur_ans=max(cur_ans,max(dp[now][0],dp[now][1]));
		ans+=cur_ans;
	}
	printf("%lld
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/fengxunling/p/10404497.html