[ZJOI2008] 骑士

[ZJOI2008] 骑士

BZOJ
luogu
我们发现关系图由恰好n个点,n条边构成,
这告诉我们关系图是一些基环树,
我们可以找出这个环,对环上每个点的子树dp一遍
再把这个环dp一遍求出答案
树dp:设(f_i)表示选择i的子树最大价值和
(g_i)表示不选i的子树最大价值和

[f_u+=g_v$$$$g_u+=max(f_v,g_v) ]

环dp:相同的状态和转移,讨论是否选1做两遍dp
博猪抠环的方法好像复杂了,不过过了就懒得管了...
注意图不一定连通和有重边

#define ll long long
#include<bits/stdc++.h>
using namespace std;
const int _=1e6+5;
int re(){
	int x=0,w=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
	return x*w;
}
int n,cnt,tot,top,sz,ts;
int h[_],cir[_],st[_],fa[_],dfn[_],low[_],tmp[_];
ll Ans,ans,g[_],f[_],F[_],G[_];
bool vis[_];
struct edge{int to,next;}e[_<<1];
void link(int u,int v){
	e[++cnt]=(edge){v,h[u]};h[u]=cnt;
	e[++cnt]=(edge){u,h[v]};h[v]=cnt;
}
void tarjan(int u){
	dfn[u]=low[u]=++ts;st[++top]=u;
	for(int i=h[u];i;i=e[i].next){
		int v=e[i].to;
		if(!dfn[v]){
			tarjan(v);
			low[u]=min(low[u],low[v]);
			if(low[v]>=dfn[u]){
				int x;sz=0;
				do{x=st[top--];tmp[++sz]=x;}while(x^v);tmp[++sz]=u;
				if(sz>tot){tot=sz;for(int j=1;j<=sz;j++)cir[j]=tmp[j];}
			}
		}
		else low[u]=min(low[u],dfn[v]);
	}
}
void dfs(int u){
	for(int i=h[u];i;i=e[i].next){
		int v=e[i].to;
		if(vis[v]||v==fa[u]||fa[v]==u)continue;//重边
		fa[v]=u;dfs(v);f[u]+=g[v];g[u]+=max(f[v],g[v]);
	}
}
int main(){
	n=re();
	for(int i=1;i<=n;i++){
		f[i]=re();link(i,re());
	}
	for(int i=1;i<=n;i++){
		if(dfn[i])continue;
		top=0;tot=0;tarjan(i);
		for(int j=1;j<=tot;j++)vis[cir[j]]=1;
		for(int j=1;j<=tot;j++){
			dfs(cir[j]);F[j]=f[cir[j]];G[j]=g[cir[j]];
		}
		G[2]+=F[1];F[3]+=G[2];G[3]+=G[2];//选择1
		for(int j=4;j<=tot;j++){
			F[j]+=G[j-1];
			G[j]+=max(F[j-1],G[j-1]);
		}
		ans=G[tot];
		for(int j=1;j<=tot;j++){
			F[j]=f[cir[j]];G[j]=g[cir[j]];
		}
		F[2]+=G[1];G[2]+=G[1];//不选择1
		for(int j=3;j<=tot;j++){
			F[j]+=G[j-1];
			G[j]+=max(F[j-1],G[j-1]);
		}
		ans=max(ans,max(F[tot],G[tot]));
		Ans+=ans;
	}
	printf("%lld
",Ans);
	return 0;
}
原文地址:https://www.cnblogs.com/sdzwyq/p/9863145.html