[ZJOI2008]骑士
P2607 [ZJOI2008]骑士
题意:
上司的舞会增强版
思路:
因为一个骑士有且只有一个最讨厌的人,而且这个骑士不会讨厌自己,即该图中是没有自环的。
利用有向图去维护信息:
我们把 \(x\) 所讨厌的人 \(y\) 设为 \(x\) 的父亲节点,这样每一个人都有且只有一条出边,每个点的出度只能为 \(1\).
考虑非根节点,又得出结论:每个连通块内有且只有一个简单环。
这样 我们考虑把每个联通块的环上删一条边,这样它必然构成树,删掉的边一定是不能同时选择的。
我们分别强制两个端点其中一个点不选,然后进行 \(dp\) ,相邻点不能选。
设 \(f[i][0/1]\) 表示以 \(i\) 为根节点的子树选/不选 \(i\) 所能获得的最大价值。
则有方程:
\[f[i][0]=\sum_{son_i} (max(f[son][0],f[son][1]))
\]
\[f[i][1]=\sum_{son_i} (f[son][0])
\]
直接写即可,感觉还挺简单的。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=2e6+5;
int head[N],ver[N],tot,nxt[N],val[N];
int vis[N],fa[N],n,root;
ll ans,f[N][2];
void add(int x,int y){
ver[++tot]=y; nxt[tot]=head[x]; head[x]=tot;
}
void dp(int x){
vis[x]=1;
f[x][0]=0,f[x][1]=val[x];
for(int i=head[x];i;i=nxt[i]){
int y=ver[i];
if(y!=root){
dp(y); f[x][0]+=max(f[y][1],f[y][0]);
f[x][1]+=f[y][0];
}
else f[y][1]=-N;
}
}
inline void find_circle(int x){
vis[x]=1;
root=x;
while(!vis[fa[root]]){
root=fa[root];
vis[root]=1;
}
dp(root);
ll t=max(f[root][0],f[root][1]);
vis[root]=1; root=fa[root];
dp(root);
ans+=max(t,max(f[root][0],f[root][1]));
}
int main(){
cin>>n;
for(int i=1,x;i<=n;i++){
cin>>val[i]>>x;
// scanf("%d%d",&val[i],&x);
add(x,i); fa[i]=x;
}
for(int i=1;i<=n;i++) if(!vis[i]) find_circle(i);
cout<<ans<<endl;
system("pause");
return 0;
}
不关注的有难了😠😠😠https://b23.tv/hoXKV9