BZOJ_2068_[Poi2004]SZP_树形DP
Description
Byteotian 中央情报局 (BIA) 雇佣了许多特工. 他们每个人的工作就是监视另一名特工. Byteasar 国王需要进行一次秘密行动,所以他要挑选尽量多的信得过的特工. 但是这项任务是如此的机密以至于所有参加行动的特工都必须至少被另一名没有参加任务的特工所监视( 就是说如果某个特工参加了行动,那么原先监视他的那些特工中至少要有一个没有参加进行动). 给出监视任务的详情,要求计算最多能有多少个特工参与其中.
Input
第一行只有一个整数, n – 特工的总数, 2 <= n <= 1000000. 特工从1 到 n编号. 接下来n行每行一个整数ak 表示特工k将要监视特工ak , 1 <= k <= n, 1 <= ak <= n, ak <> k.
Output
打印一个数,最多能有多少特工参加入这个任务.
Sample Input
6
2
3
1
3
6
5
2
3
1
3
6
5
Sample Output
3
HINT
基环树DP,把环断开,让一个强制不选,然后跑两次DP。
每次DP的求法类似一个最小支配集。
代码:
#include <stdio.h> #include <string.h> #include <algorithm> using namespace std; #define N 1000050 int head[N],to[N<<1],nxt[N<<1],cnt,fa[N],root[N],kill[N],n,f[N],g[N],le; inline void add(int u,int v) { to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt; } int find(int x) {return fa[x]==x?x:fa[x]=find(fa[x]);} void dfs(int x) { int i,minn=100000,ok=0,maxx=0; f[x]=1; g[x]=0; for(i=head[x];i;i=nxt[i]) { if(to[i]!=le) dfs(to[i]); g[x]+=max(g[to[i]],f[to[i]]); maxx+=max(g[to[i]],f[to[i]]); if(g[to[i]]>=f[to[i]]) ok=1; else minn=min(minn,f[to[i]]-g[to[i]]); } if(ok) f[x]+=maxx; else f[x]+=maxx-minn; } int main() { scanf("%d",&n); int i,x,y,ans=0; for(i=1;i<=n;i++) fa[i]=i; for(i=1;i<=n;i++) { scanf("%d",&x); int dx=find(x),di=find(i); if(dx!=di) { fa[di]=dx; add(x,i); }else { root[dx]=x; kill[dx]=i; } } for(i=1;i<=n;i++) { if(fa[i]==i) { dfs(root[i]); le=root[i]; dfs(kill[i]); int t=f[kill[i]]; f[root[i]]=g[root[i]]+1; dfs(kill[i]); ans+=max(t,g[kill[i]]); } } printf("%d ",ans); }