1. 程式人生 > 其它 >P2018:訊息傳遞題解——二次掃描與換根

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]\)

表示將\(x\)染色後染完整個\(x\)的子樹的最小時間(為了方便計算,這裡沒有將選擇\(x\)的“1”計入,當然最後將答案加一即可)。那麼參考這個中關於貪心的證明部分,容易知道:

\[f[x]=\max_{y\in son(x)}\lbrace f[y]+Rank_{y}\rbrace \]

其中\(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<<" ";
}