#(树形DP+基环树)洛谷P2607 [ZJOI2008]骑士(省选/NOI-)

题目描述

Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英。他们劫富济贫,惩恶扬善,受到社会各界的赞扬。

最近发生了一件可怕的事情,邪恶的Y国发动了一场针对Z国的侵略战争。战火绵延五百里,在和平环境中安逸了数百年的Z国又怎能抵挡的住Y国的军队。于是人们把所有的希望都寄托在了骑士团的身上,就像期待有一个真龙天子的降生,带领正义打败邪恶。

骑士团是肯定具有打败邪恶势力的能力的,但是骑士们互相之间往往有一些矛盾。每个骑士都有且仅有一个自己最厌恶的骑士(当然不是他自己),他是绝对不会与自己最厌恶的人一同出征的。

战火绵延,人民生灵涂炭,组织起一个骑士军团加入战斗刻不容缓!国王交给了你一个艰巨的任务,从所有的骑士中选出一个骑士军团,使得军团内没有矛盾的两人(不存在一个骑士与他最痛恨的人一同被选入骑士军团的情况),并且,使得这支骑士军团最具有战斗力。

为了描述战斗力,我们将骑士按照1至N编号,给每名骑士一个战斗力的估计,一个军团的战斗力为所有骑士的战斗力总和。

输入格式

输入文件knight.in第一行包含一个正整数N,描述骑士团的人数。

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

输出格式

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

输入输出样例

输入 #1
3
10 2
20 3
30 1
输出 #1
30

说明/提示

对于30%的测试数据,满足N ≤ 10;

对于60%的测试数据,满足N ≤ 100;

对于80%的测试数据,满足N ≤ 10 000。

对于100%的测试数据,满足N ≤ 1 000 000,每名骑士的战斗力都是不大于 1 000 000的正整数。

则对每个基环树(联通块)将答案累加上max(f[ui][0], g[vi][0])max(f[ui][0],g[vi][0]) 即可

注意以下几个坑点:

1.要开long long

2.组成的是基环树森林而非单棵基环树

3.判断不走断开的边时必须用边来判断,不能用点,因为两个骑士可能相互憎恨,形成重边,这时如果判断点,会造成树不联通。

/*
1.问题转化:一个环套树上有点权,求最大的节点选择方案使
点权最大,且各个节点不存在相邻位置
2.首先,可以通过一次拓扑排序找出环的位置
3.对于非环部分,也就是树的部分
遍历整个环:
如果这个节点有子节点,就考虑树形DP
设dp[x][0]表示以x为根的子树所有的最大权值,且x节点不选
dp[x][1]表示以x为根的子树所有的最大权值,且x节点选择
记忆化搜索进行树形Dp
如果选x节点,
if v∈son[x]
dp[x][1]=max(dp[v][0]+val[x],dp[x][1]);
dp[x][0]=max(dp[x][0],max(dp[v][0],dp[v][1]));
4.考虑环的部分:
使用vector存储环内的所有点,遍历所有环上点
dp[x][0]表示不选择x点的部分的值,继承树根信息!
破环成链?
for(int i=len+1;i<=2*len;i++)
dp[i][0]=dp[i-len][0],dp[i][1]=dp[i-len][1];

for(int i=1;i<=2*n-len;i++)
{
j=i+len-1;
for(int k=i;k<=j;k++)

}
*/
#include<cstdio>
#include<algorithm>
#include<iostream>
#define N 1000002
#define ll long long
using namespace std;
ll ans;
int ui,vi,E;
struct node{
int from;
int to;
int next;
}e[N<<1];
int head[N],num;
void addedge(int from,int to)
{
e[++num].from=from;
e[num].next=head[from];
e[num].to=to;
head[from]=num;
}
int x;
bool vis[N],used[N],vis1[N],vis2[N],suc;
ll f[N][3],g[N][3],val[N];

void fg(int x)
{
used[x]=1;
for(int i=head[x];i;i=e[i].next)
{
int v=e[i].to;
if(!used[v])
fg(v);
}
}
int n;
void find_circle(int x,int fa)
{
if(suc) return;
vis[x]=1;
for(int i=head[x];i;i=e[i].next)
{
int v=e[i].to;
if(vis[v]&&v!=fa)
{
ui=x;
vi=v;
E=i;
return;
}
if(!vis[v])
{
find_circle(v,x);
}
}
}
inline void dfs1(int x)
{
vis1[x]=1;
f[x][1]=val[x];
f[x][0]=0;
for(int i=head[x];i;i=e[i].next)
{
int v=e[i].to;
if(!vis1[v]&&i!=E)
{
dfs1(v);
f[x][0]+=max(f[v][0],f[v][1]);
f[x][1]+=f[v][0];
}
}
return;
}
inline void dfs2(int x)
{
vis2[x]=1;
g[x][1]=val[x];
g[x][0]=0;
for(int i=head[x];i;i=e[i].next)
{
int v=e[i].to;
if(!vis2[v]&&i!=E)
{
dfs2(v);
g[x][0]+=max(g[v][0],g[v][1]);
g[x][1]+=g[v][0];
}
}
return;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld %d",&val[i],&x);
addedge(i,x);
addedge(x,i);
}
for(int i=1;i<=n;i++)
{
if(!used[i])
{
suc=0;
fg(i);
find_circle(i,0);
dfs1(ui);
dfs2(vi);
ans+=max(f[ui][0],g[vi][0]);
}
}
printf("%lld",ans);
return 0;
}

需吸氧233~

猜你喜欢

转载自www.cnblogs.com/little-cute-hjr/p/11625488.html