P2018:訊息傳遞題解——二次掃描與換根
訊息傳遞
題面
題目描述
巴蜀國的社會等級森嚴,除了國王之外,每個人均有且只有一個直接上級,當然國王沒有上級。如果A是B的上級,B是C的上級,那麼A就是C的上級。絕對不會出現這樣的關係:A是B的上級,B也是A的上級。
最開始的時刻是0,你要做的就是用1單位的時間把一個訊息告訴某一個人,讓他們自行散佈訊息。在任意一個時間單位中,任何一個已經接到訊息的人,都可以把訊息告訴他的一個直接上級或者直接下屬。
現在,你想知道:
1.到底需要多長時間,訊息才能傳遍整個巴蜀國的所有人?
2.要使訊息在傳遞過程中消耗的時間最短,可供選擇的人有那些?
輸入格式
輸入檔案的第一行為一個整數N(N≤1000),表示巴蜀國人的總數,假如人按照1到n編上了號碼,國王的編號是1。第2行到第N行(共N-1行),每一行一個整數,第i行的整數表示編號為i的人直接上級的編號。
輸出格式
檔案輸出共計兩行:
第一行為一個整數,表示最後一個人接到訊息的最早時間。
第二行有若干個數,表示可供選擇人的編號,按照編號從小到大的順序輸出,中間用空格分開。
樣例 #1
樣例輸入 #1
8
1
1
3
4
4
4
3
樣例輸出 #1
5
3 4 5 6 7
題解
題意簡述:給定一棵樹,其中1是樹根,要找到一個點將其染色,耗費1的時間,然後在每一個時間內,所有被染色點可以在與其相連的點中選擇一個染色,將整棵樹都被染完的時間即為這個點的答案。要求出最小的答案,並且指出哪些點的答案最優。
對於此類問題,我們先來思考如果指定了一個點如何求出答案。
首先明確,由於一個點可以向上向下傳遞,那麼此時誰是父親誰是兒子不重要了,我們只需要知道與這個點連邊的點可以被這個點染色即可,那麼我們可以將這個點變為樹根,顯然此時只能向下傳遞,此時這個問題就很像一個樹形DP了,具體的,設\(f[x]\)
其中\(son(x)\)代表\(x\)的子節點集合,\(Rank_y\)是指在\(x\)的所有兒子節點中\(f\)值的排名(從大到小)。\(O(n\log_2 n)\)DP即可求解,這個上界很鬆,故其實\(O(n^2\log_2n)\)的列舉即可AC。
但顯然,進行\(n\)次DP肯定有優化的空間。考慮二次掃描與換根法,請看這圖:
對於這部分的計算,可以設\(g[x]\)表示在\(father(x)\)為根的樹(整棵樹)中,除了以\(x\)為根的子樹的答案,對於這個的計算,可以類比\(f\),也很簡單:
\[g[x]=\max\lbrace g[fa(x)]+Rank_{fa(x)},\max_{y\in son(x)}\lbrace f[y]+Rank_y\rbrace\rbrace \]其中\(fa\)表示父節點,\(Rank\)表示將所有的\(f[y](y\in son(x))\)與\(g[fa(x)]\)放在一起從大到小排序後的排名。
有了\(g\),\(ans[x]\)也就呼之欲出了:
\[ans[x]=1+\max\lbrace g[x]+Rank_x,\max_{y\in son(x)}\lbrace f[y]+Rank_y\rbrace\rbrace \]\(Rank\)含義類似。
進行兩次DP即可求出解,需要注意的是,對於根節點\(1\),不存在\(g\),也不計入子節點統計\(g\)的\(g[fa(x)]\)。
加一的原因是無論是\(f[x],g[x]\)都是預設\(x\)已染色,但實際上需要1的時間將其染色。
#define N 2005
int head[N],nxt[N],ver[N],tot=1,f[N],g[N],ans[N],b[N],lst[N],n;
void add(int u,int v){
nxt[++tot]=head[u],ver[head[u]=tot]=v;
}
void dfs1(int u,int fa){
int cnt=0;
lst[u]=fa;
for(int i=head[u];i;i=nxt[i]){
int v=ver[i];
if(v==fa)continue;
dfs1(v,u);
}
for(int i=head[u];i;i=nxt[i]){
if(ver[i]==fa)continue;
b[++cnt]=f[ver[i]];
}
sort(b+1,b+cnt+1);
reverse(b+1,b+cnt+1);
for(int i=1;i<=cnt;i++){
f[u]=max(f[u],b[i]+i);
}
}
void dfs2(int u,int fa){
int cnt=0;
if(fa!=1)b[++cnt]=g[fa];
for(int i=head[fa];i;i=nxt[i]){
int v=ver[i];
if(v==u||v==lst[fa])continue;
b[++cnt]=f[v];
}
sort(b+1,b+cnt+1);
reverse(b+1,b+cnt+1);
for(int i=1;i<=cnt;i++){
g[u]=max(g[u],b[i]+i);
}
cnt=1;b[1]=g[u];
for(int i=head[u];i;i=nxt[i]){
int v=ver[i];
if(v==fa)continue;
b[++cnt]=f[v];
}
sort(b+1,b+cnt+1);
reverse(b+1,b+cnt+1);
for(int i=1;i<=cnt;i++){
ans[u]=max(ans[u],b[i]+i);
}
for(int i=head[u];i;i=nxt[i]){
int v=ver[i];
if(v==fa)continue;
dfs2(v,u);
}
}
int main(){
ios::sync_with_stdio(false);
cin>>n;
for(int i=2;i<=n;i++){
int v;
cin>>v;
add(i,v);add(v,i);
}
dfs1(1,0);
ans[1]=f[1];
for(int i=head[1];i;i=nxt[i]){
int v=ver[i];
dfs2(v,1);
}
int Ans=0x3f3f3f3f;
for(int i=1;i<=n;i++)Ans=min(Ans,++ans[i]);
cout<<Ans<<endl;
for(int i=1;i<=n;i++)if(ans[i]==Ans)cout<<i<<" ";
}