[bzoj3037/2068]創世紀[Poi2004]SZP_樹形dp_並查集_基環樹
阿新 • • 發佈:2018-04-20
%d turn line dfs 而且 ring name ... find
創世紀 SZP bzoj-3037/2068 Poi-2004
題目大意:給你n個物品,每個物品可以且僅可以控制一個物品。問:選取一些物品,使得對於任意的一個被選取的物品來講,都存在一個沒有被選取的物品,而且選取的個數最大。
註釋:$1\le n \le 10^6$。
想法:顯然,和騎士類似的,是一個基環樹森林。如果A物品可以控制B物品,那就有B物品向A物品連邊。對於每一個基環樹,如果這個基環樹是樹的話顯然變成樹形dp入門題,暴力樹形dp即可。然後對於基環樹來講,我們依然記錄環上兩點,分別以這兩點為根,然後特判樹形dp即可。
最後,附上醜陋的代碼... ...
#include <cstdio> #include <cstring> #include <iostream> #define N 1000010 using namespace std; int n,m,ans,now,tot; int to[N],nxt[N],head[N],f[N],g[N],fa[N],ra[N],rb[N]; inline void add(int x,int y) { to[++tot]=y; nxt[tot]=head[x]; head[x]=tot; } int find(int x) { return (fa[x]==x)?x:(fa[x]=find(fa[x])); } void dfs(int x) { int t=1<<30; g[x]=0; for(int i=head[x];i;i=nxt[i]) { if(to[i]!=now) dfs(to[i]); g[x]+=max(f[to[i]],g[to[i]]); t=min(t,max(f[to[i]],g[to[i]])-g[to[i]]); } f[x]=g[x]+1-t; } int main() { scanf("%d",&n); int a; for(int i=1;i<=n;i++) fa[i]=i; for(int i=1;i<=n;i++) { scanf("%d",&a); if(find(a)!=find(i)) { add(a,i); fa[fa[a]]=fa[i]; } else ra[++m]=a,rb[m]=i; } for(int i=1;i<=m;i++) { dfs(ra[i]),now=ra[i]; dfs(rb[i]),a=f[rb[i]]; f[ra[i]]=g[ra[i]]+1; dfs(rb[i]),ans+=max(a,g[rb[i]]); } printf("%d",ans); return 0; }
小結:基環樹dp是一種常見的,樹形dp帶基環樹的處理方法。
[bzoj3037/2068]創世紀[Poi2004]SZP_樹形dp_並查集_基環樹