【题解】洛谷P2607【ZJOI2008】骑士

时间:2023-03-09 16:48:12
【题解】洛谷P2607【ZJOI2008】骑士

洛谷P2607:https://www.luogu.org/problemnew/show/P2607

一道毒瘤的环基树问题

第一次做环基树的题目

刚看题目的时候觉得不就是跟没有上司的舞会一样嘛

然后看着样例画了个图发现!!!

居然有环!!受到惊吓的蒟蒻

后来查了一下原来是叫环基树

思路

由于每个骑士有且仅有一个仇恨对象

So 整个图里有且只有一个环而且这个环必过根节点(为什么?)

把每个人的仇人设置为他的 父亲???

所以每个点的出度为1 那么根节点本来应该是0 但是为1 说明根节点在环中

我们只需要找出图中的环

遍历其中的点设为根节点

那么我们只需要在答案中加上取根节点不取其父亲和取父亲不取根节点中最大的

其他的按照:

  • 取此点的话 他的儿子都不能来
  • 不取此点 他的儿子要来不来都可以 显然取最大
  • f[i][0]不取此点 f[i][1]取此点

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 2000000
#define ll long long
using namespace std;
int n,cnt,root;
ll ans;
ll f[maxn][],head[maxn],val[maxn],vis[maxn],fa[maxn];
struct Edge
{
int next;
int to;
}e[maxn];
void add(int u,int v)
{
e[++cnt].to=v;
e[cnt].next=head[u];
head[u]=cnt;
}
void dp(int x)
{
vis[x]=;
f[x][]=;
f[x][]=val[x];//初始化
for(int i=head[x];i;i=e[i].next)
{
int v=e[i].to;
if(v!=root)//如果不是根节点
{
dp(v);
f[x][]+=max(f[v][],f[v][]);//如果不取此点 儿子爱来不来
f[x][]+=f[v][]; //如果取此点 儿子都不能来
}
else
f[x][]=-maxn;//如果是根节点 那么此点不能来设为最小值
}
}
void find(int x)
{
vis[x]=;
root=x;
while(!vis[fa[root]])
{
root=fa[root];
vis[root]=;
}//找环
dp(root);
ll t=max(f[root][],f[root][]);//取最大值
vis[root]=;
root=fa[root];//重新定义根为根节点的其父亲
dp(root);
ans+=max(t,max(f[root][],f[root][]));//取最大值
return;
}
int main()
{
scanf("%d",&n);
for(int i=;i<=n;i++)
{
int x;
scanf("%lld%d",&val[i],&x);
add(x,i);
fa[i]=x;
}
for(int i=;i<=n;i++)
if(!vis[i])
find(i);//找环
printf("%lld",ans);
}