[BZOJ 1040] 骑士(基环森林DP)

由题目可知,每个点的出度都为1,则只可能是树或者是基环内向树,但是全图不一定联通,所以可能是基环森林,那么我们对于每一颗树,用dfs找环,找到环的入口处的两个端点即可做两次树形DP,取较大值加到总答案中即可。
以下是代码:

#include<bits/stdc++.h>
#define maxn  1000007
#define out(x) printf("%d",x)
#define in(x) scanf("%d",&x)
using namespace std;

int n,cnt=1,val[maxn],head[maxn];
long long f[maxn][2];
bool vis[maxn];

struct edge{
	int nxt;
	int to;
}e[maxn<<1];

void add(int u,int v){
	e[++cnt].to=v;
	e[cnt].nxt=head[u];
	head[u]=cnt;
}

int r1,r2,nops;

void dfs(int u,int fa){
	vis[u]=1;
	for(int v,i=head[u];i;i=e[i].nxt){
		v=e[i].to;
		if(v==fa) continue;
		if(!vis[v]) dfs(v,u);
		else {
			r1=u;r2=v;
			nops=i;
		}
	}
}

void dp(int u,int fa){
	f[u][0]=0;f[u][1]=val[u];
	for(int v,i=head[u];i;i=e[i].nxt){
		v=e[i].to;
		if(v==fa) continue;
		if(i==nops||i==(nops^1)) continue;
		dp(v,u);
		f[u][1]+=f[v][0];
		f[u][0]+=max(f[v][0],f[v][1]);
	}
}

int main(){
	in(n);
	for(int i=1,v;i<=n;i++){
		in(val[i]);in(v);
		add(i,v);add(v,i);
	}
	long long tp,ans=0;
	for(int i=1;i<=n;i++){
		if(vis[i]) continue;
		dfs(i,-1);
		dp(r1,-1);
		tp=f[r1][0];
		dp(r2,-1);
		ans+=max(tp,f[r2][0]);
	}
	printf("%lld",ans);
	return 0;
} 
posted @ 2018-09-08 14:35  IEQEFCR  阅读(120)  评论(0编辑  收藏  举报