[ACM]【樹形DP/LCA】牛客練習賽62 牛牛染顏色

牛牛染顏色

傳送門
題意:給出一個有根樹,求滿足條件的染色方案的數目,條件:任意兩個染黑的點的LCA必須也爲黑點。
在這裏插入圖片描述

思路:

我一看到LCA就會想到自己還不會倍增的恐懼 很顯然的樹形DP。既然是DP,就只用考慮初始狀態和狀態轉移。
狀態轉移方程:(dp[u][1]爲將u點染黑,dp[u][0]爲不染)
dp[u][0]=1+vson[u](dp[v][1]+dp[v][0]1)dp[u][0]=1+\sum_{v\in son[u]}^{}(dp[v][1]+dp[v][0]-1)
dp[u][1]=vson[u](dp[v][1]+dp[v][0])dp[u][1]=\prod_{v\in son[u]}^{}(dp[v][1]+dp[v][0])
初始狀態:
dp[u][0]=dp[u][1]=1dp[u][0]=dp[u][1]=1

對於狀態轉移方程的解釋:

當點u不塗黑時,如果u有兩個或以上子樹有塗黑的點,那麼就不滿足“任意兩個黑節點的LCA也是黑色的”。因此,只能有至多一個子樹有塗黑的點。當塗黑的點存在於以v爲根的子樹中,其他子樹必然都沒塗黑,因此情況數等於以v爲根的子樹的總情況數:dp[v][1]+dp[v][0]dp[v][1]+dp[v][0]。每個子樹都有可能成爲那個特殊的子樹,因此情況累加。但是,我們要注意,dp[v][0]dp[v][0]包含以v爲根的子樹都不塗黑的情況(而此時其他樹也都沒有黑點:即空集),每加上這樣一項,都加上了一個空集,重複了。於是每加這樣一項都把空集減去(-1)。空集在最外面加回來就行了。

當點u塗黑時,點u的所有兒子都可以選擇塗黑或不塗,這樣都滿足LCA也爲黑色。那麼情況數就等於每個子樹的合法情況數目之間相乘的(因爲相互不影響)。每棵子樹的情況數目等於子樹的根v塗與不塗的情況數目之和。

初始狀態:塗黑算一種情況,不塗也是一種情況。

代碼:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int maxn=1000005;
ll head[maxn],tot=0;
ll dp[maxn][3];
struct node{
	int to,next;
}e[maxn*2];
void adde(int u,int v){
	++tot;
	e[tot].next=head[u];
	e[tot].to=v;
	head[u]=tot;
}
inline int read(){
	int f=1,x=0;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=x*10+ch-'0';
		ch=getchar();
	}
	return x*f;
}
//運用dfs
void dfs(int u,int fa){
	dp[u][0]=dp[u][1]=1;
	for(int i=head[u];i;i=e[i].next){
		int v=e[i].to;
		if(v==fa) continue;
		dfs(v,u);
		dp[u][1]=(dp[u][1]*(dp[v][0]+dp[v][1]))%mod;
		dp[u][0]=(dp[u][0]+dp[v][0]+dp[v][1]-1)%mod;
	}
}
int main(){
	int n=read();
	for(int i=1;i<=n-1;i++){
		int u=read(),v=read();
		adde(u,v);
		adde(v,u);
	}
	dfs(1,0);
	printf("%lld\n",(dp[1][0]+dp[1][1])%mod);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章