[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);
}
点击查看代码
#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);
}

浙公网安备 33010602011771号