[BZOJ 1040][ZJOI2008]骑士

时间:2023-11-24 21:39:08

1040: [ZJOI2008]骑士

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 5403  Solved: 2060
[Submit][Status][Discuss]

Description

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

Input

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

Output

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

Sample Input

3
10 2
20 3
30 1

Sample Output

30

HINT

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

题解

分析题意可以得到这应该是个最大权独立集...

然而普适的最大权独立集问题并不可做...这个问题肯定有特殊的地方. 我们发现每个点主动连出去的边有且仅有一条, 我们可以发现这是个环套树.

如果是树的话直接一个 $DP$ 怼上去就可以了, 而对于环套树, 我们可以先进行一遍 $DFS$ 找找是否有环, 没有环直接按树来 $DP$ , 如果有环的话, 任意选一条边断掉转化成树, 标记这条边连接的两个结点, 两个结点中强制某一个不能选, $DP$ 一遍, 换另一个结点强制不选再跑一遍, 在两种情况下取最大值.

断边的过程可以通过维护全局标记实现, 对于与全局标记相等的边直接不走就可以了, 对于强制不选, 则可以在跑一遍 $DP$ 后强行将强制不选的那个结点选中时得到的最大值赋为 $-\infty$ 即可

参考代码

GitHub

 #include <cstdio>
#include <cstring>
#include <climits>
#include <cstdlib>
#include <iostream>
#include <algorithm> const int MAXN=1e6+; struct Edge{
int from;
int to;
int ID;
Edge* next;
};
Edge E[MAXN*];
Edge* head[MAXN];
Edge* top=E; int n;
int disableV;
int disableE;
int cut1,cut2;
long long ans,tmp;
bool visited[MAXN];
long long val[MAXN];
long long dp[MAXN][]; void DP(int,int);
void DFS(int,int);
void Initialize();
void Insert(int,int,int); int main(){
Initialize();
for(int i=;i<=n;i++){
if(visited[i])
continue;
else{
DFS(i,);
disableV=cut1;
DP(i,);
tmp=std::max(dp[i][],dp[i][]);
disableV=cut2;
DP(i,);
tmp=std::max(tmp,std::max(dp[i][],dp[i][]));
ans+=tmp;
}
}
printf("%lld\n",ans);
return ;
} void DFS(int root,int prt){
visited[root]=true;
for(Edge* i=head[root];i!=NULL;i=i->next){
if(i->ID==prt)
continue;
else if(visited[i->to]){
cut1=root;
cut2=i->to;
disableE=i->ID;
}
else
DFS(i->to,i->ID);
}
} void DP(int root,int prt){
dp[root][]=;
dp[root][]=val[root];
for(Edge* i=head[root];i!=NULL;i=i->next){
if(i->ID==disableE||i->ID==prt)
continue;
DP(i->to,i->ID);
dp[root][]+=std::max(dp[i->to][],dp[i->to][]);
dp[root][]+=dp[i->to][];
}
if(root=disableV)
dp[root][]=LLONG_MIN;
} void Initialize(){
scanf("%d",&n);
int tmp;
for(int i=;i<=n;i++){
scanf("%d%d",val+i,&tmp);
Insert(i,tmp,i);
Insert(tmp,i,i);
}
} inline void Insert(int from,int to,int ID){
top->to=to;
top->ID=ID;
top->from=from;
top->next=head[from];
head[from]=top++;
}

Backup

[BZOJ 1040][ZJOI2008]骑士