[oiclass4094] 骑士:基环树

问题描述

\(Z\) 国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英。他们劫富济贫,惩恶扬善,受到社会各界的赞扬。最近发生了一件可怕的事情,邪恶的 \(Y\) 国发动了一场针对 \(Z\) 国的侵略战争。
战火绵延五百里,在和平环境中安逸了数百年的 \(Z\) 国又怎能抵挡的住 \(Y\) 国的军队。于是人们把所有的希望都寄托在了骑士团的身上,就像期待有一个真龙天子的降生,带领正义打败邪恶。骑士团是肯定具有打败邪恶势力的能力的,但是骑士们互相之间往往有一些矛盾。每个骑士都有且仅有一个自己最厌恶的骑士(当然不是他自己),他是绝对不会与自己最厌恶的人一同出征的。
战火绵延,人民生灵涂炭,组织起一个骑士军团加入战斗刻不容缓!国王交给了你一个艰巨的任务,从所有的骑士中选出一个骑士军团,使得军团内没有矛盾的两人(不存在一个骑士与他最痛恨的人一同被选入骑士军团的情况),并且,使得这支骑士军团最具有战斗力。为了描述战斗力,我们将骑士按照 \(1\)\(N\) 编号,给每名骑士一个战斗力的估计,一个军团的战斗力为所有骑士的战斗力总和。

输入格式

第一行包含一个正整数 \(N\),描述骑士团的人数。
接下来 \(N\)行,每行两个正整数,按顺序描述每一名骑士的战斗力和他最痛恨的骑士。

输出格式

应包含一行,包含一个整数,表示你所选出的骑士军团的战斗力。

输入样例

3
10 2
20 3
30 1

输出样例

30

数据范围

\(N ≤ 1 000 000\),每名骑士的战斗力都是不大于 \(1 000 000\) 的正整数。

题解

考虑一个连通图,如果是一棵树,则该问题等价于求树上的最大独立集。当然,图上有可能出现环,根据题意,每个连通图上只可能出现一个环,即基环树。考虑基环树的情况,可以先找出基环树上的环,将环上的一条边断开,形成一棵树,对新树求树上最大独立集即可。对于断开的边u-v,因为两个点不能同时选,所以分别考虑u不选的情况和v不选的情况,即f[u][0]和f[v][0]。对每个连通块分别求一次即可。

代码

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1000000+6;
vector<int> g[N];
int n,val[N],vis[N],x,l,r;
bool flag;
long long ans,f[N][2];
void findCircle(int u,int fa){
	vis[u]=1;
	for(int i=0;i<g[u].size();i++){
		int v=g[u][i];
		if(v==fa)continue;
		if(vis[v]){
			flag=true;
			l=u;
			r=v;
			continue;
		}
		findCircle(v,u);
	}
}
void dfs(int u,int fa){
	f[u][0]=1LL*0;
	f[u][1]=1LL*val[u];
	for(int i=0;i<g[u].size();i++){
		int v=g[u][i];
		if(v==fa)continue;
		if((u==l&&v==r)||(u==r&&v==l))continue;
		dfs(v,u);
		f[u][0]+=max(f[v][0],f[v][1]);
		f[u][1]+=f[v][0];
	}
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d %d",&val[i],&x);
		g[i].push_back(x);
		g[x].push_back(i);
	}
	for(int i=1;i<=n;i++){
		if(!vis[i]){
			flag=false;
			findCircle(i,-1);
			dfs(l,-1);
			long long tmp=f[l][0];
			dfs(r,-1);
			ans+=max(tmp,f[r][0]);
		}
	}
	printf("%lld",ans);
}
以上代码只能取得40分,原因是图上有重边,对待重边短边,我们需要采用链式前向星的方式存图,然后记住边号再根据边号断边。以下是AC代码:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1000000+5;
int n,val[N],e[N*2],head[N],nxt[N*2],vis[N],tot,l,r,edge;
long long ans,f[N][2];
void addedge(int u,int v){
	e[tot]=v;
	nxt[tot]=head[u];
	head[u]=tot;
	tot++;	
}
void find_circle(int u,int fa){
	vis[u]=1;
	for(int i=head[u];i!=-1;i=nxt[i]){
		int v=e[i];
		if(v==fa)continue;
		if(vis[v]){
			edge=i;
			l=u;
			r=v;
		}else{
			find_circle(v,u);
		}
	}
}
void dfs(int u,int fa){
	f[u][0]=0;
	f[u][1]=val[u];
	for(int i=head[u];i!=-1;i=nxt[i]){
		int v=e[i];
		if(v==fa)continue;
		if(i==edge||i==(edge^1))continue;
		dfs(v,u);
		f[u][0]+=max(f[v][0],f[v][1]);
		f[u][1]+=f[v][0];
	}
}
int main(){
	memset(head,-1,sizeof(head));
	scanf("%d",&n);
	for(int i=1,x;i<=n;i++){
		scanf("%d %d",&val[i],&x);
		addedge(i,x);
		addedge(x,i);
	}
	
	for(int i=1;i<=n;i++){
		if(!vis[i]){
			find_circle(i,-1);
			dfs(l,-1);
			long long tmp=f[l][0];
			dfs(r,-1);
			ans+=max(tmp,f[r][0]);
		}
	}
	printf("%lld",ans);
}
posted @ 2022-02-24 19:56  chxulong  阅读(66)  评论(0)    收藏  举报