BZOJ 1040: [ZJOI2008]骑士

基环树上的“没有上司的舞会”。
很显然,最终的图是若干个联通块,那么在读入的时候用并查集进行维护,如果两个点不在同一个联通块内就建一条双向边,如果在同一个联通块内,那么就记录该联通块的两端点的此时两点。
把每个联通块分开来做,对于没个联通块的两个端点均做一次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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值