[AGC009B Tournament] [建模+貪心]
阿新 • • 發佈:2018-12-10
[題目大意]
n個人參加錦標賽,比賽有若干場,每場都是兩個倖存者比試,淘汰失敗方。現在不清楚具體怎麼比的,只知道1號為最終冠軍,其餘每個人,i號是被a[i]打敗的,讓你在所有可能的比賽流程樹中找出深度最小的那個,輸出深度。
[思路]
考慮建一個新樹,其中i的父親是a[i],這個時候我們發現,i的子樹中只有i倖存下來,接下來的比賽和i的後代沒有任何關係,即沒有後效性,所以我們顯然可以貪心地記錄一個g[i],表示i的子樹全部比完後,形成的以i為根的競賽樹的深度dep[i]的最小值,接著向上合併。
討論一個點有多個兒子的情況。觀察發現,這個點x會依次和每個兒子j節點比一場,每一次比賽相當於令dep[x]=max(dep[x],g[j])+1,我們只需要找到一個最優合併順序,使得dep[x]儘量小就可以了。不難證明最優策略是按g[j]從小到大的順序依次合併。
複雜度O(nlogn)。
#include <cstdio> #include <algorithm> #define rep(i,j,k) for (i=j;i<=k;i++) using namespace std; const int N=1e5+5; int n,i,l,r,x,y,f,bn,son[N],fs[N],bro[N],fa[N],q[N],b[N],g[N]; void add(int x,int y) { bro[y]=fs[x]; fs[x]=y; } int main() { //freopen("tournament.in","r",stdin); //freopen("tournament.out","w",stdout); scanf("%d",&n); rep(i,2,n) scanf("%d",&fa[i]),son[fa[i]]++,add(fa[i],i); rep(i,1,n) if (!son[i]) q[++r]=i; for (l=1;l<=r;l++) { x=q[l]; if (fs[x]) { for (y=fs[x],bn=0;y;y=bro[y]) b[++bn]=g[y]; sort(b+1,b+1+bn); rep(i,1,bn) g[x]=max(g[x],b[i])+1; } if (x==1) break; f=fa[x]; son[f]--; if (!son[f]) q[++r]=f; } printf("%d\n",g[1]); return 0; }