BZOJ 1040: [ZJOI2008]骑士

坚强是说给别人听的谎言 提交于 2020-01-07 15:40:51
基环树上的“没有上司的舞会”。
很显然,最终的图是若干个联通块,那么在读入的时候用并查集进行维护,如果两个点不在同一个联通块内就建一条双向边,如果在同一个联通块内,那么就记录该联通块的两端点的此时两点。
把每个联通块分开来做,对于没个联通块的两个端点均做一次dp,注意,最后的答案统计应该是max(f[num[i].x][0],f[num[i].y][0])。
为什么端点不选?在做dp过程中,我们不知道和端点相连的那个另一个端点是否被选了,即使知道的话,由于要维护dp的正确性,也不好控制不选,所以就强制了端点不选。
两个端点,不可能同时选择,所以这样做是合法的,进行两次操作,每次强制一个端点不选,那么另一个端点的选与不选,就完全依靠dp了。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+5;
int n,v,tot,now,ans,xx,yy;
int c[N],f[N],dp[N][2];
int cnt,head[N];
struct edge{int next,to;}e[N<<1];
struct node{int x,y;}num[N];

int find(int x)
{
	if (f[x]==x) return x;
	return f[x]=find(f[x]);
}

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

void dfs(int u,int fa)
{
	dp[u][0]=dp[u][1]=0;
	dp[u][1]=c[u];
	for (register int i=head[u]; i; i=e[i].next)
	if (e[i].to!=fa)
	{
		dfs(e[i].to,u);
		dp[u][0]+=max(dp[e[i].to][0],dp[e[i].to][1]);
		dp[u][1]+=dp[e[i].to][0];
	}
}

signed main(){
	scanf("%lld",&n);
	for (register int i=1; i<=n; ++i) f[i]=i;
	for (register int i=1; i<=n; ++i) 
	{
		scanf("%lld%lld",&c[i],&v);
		xx=find(i); yy=find(v);
		if (xx!=yy) 
		{
			f[xx]=yy;
			add(i,v); add(v,i); 
		}
		else tot++,num[tot].x=i,num[tot].y=v;
	}
	for (register int i=1; i<=tot; ++i)
	{
		now=0;
		dfs(num[i].x,0);
		now=max(now,dp[num[i].x][0]);
		dfs(num[i].y,0); 
		now=max(now,dp[num[i].y][0]);
		ans+=now;
	}
	printf("%lld\n",ans);
return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!