1. 程式人生 > 其它 >Codeforces 1067E - Random Forest Rank(找性質+樹形 dp)

Codeforces 1067E - Random Forest Rank(找性質+樹形 dp)

找性質+樹形 dp

Codeforces 題面傳送門 & 洛谷題面傳送門

一道不知道能不能算上自己 AC 的 D1E(?)

挺有意思的結論題,結論倒是自己猜出來了,可根本不會證(

開始搬運題解 ing:

碰到這樣的題我們肯定要考慮一個圖鄰接矩陣的秩是什麼。顯然根據我們幼兒園就學過的線性代數,對於一個矩陣 \(A\)​ 而言,其行列式就是其最大的子式滿足其行列式不等於 \(0\),也就是任取若干行 & 若干列,它們的交組成的矩陣行列式不等於 \(0\),不難發現對於一個森林的鄰接矩陣而言,對於任意一個子式,如果它的行列式不等於 \(0\),那麼它的行、列下標的並組成的主子式的行列式肯定也非零,因此我們只用考慮其主子式就行了,而主子式顯然可以看作匯出子圖的鄰接矩陣,因此我們接下來只需探究什麼樣的矩陣行列式非零。

考慮行列式的展開式:

\[\det A=\sum\limits_{p}(-1)^{\tau(p)}\prod\limits_{i=1}^nA_{i,p_i} \]

顯然對於後面的 \(\prod\)​ 而言,只有 \((i,p_i)\)​ 存在邊相連時 \(A_{i,p_i}\)​ 才非零,而由於圖無自環,如果 \(\exists i,s.t.i=p_i\)​ 必然有 \(p\)​ 對 \(\det A\)​ 的貢獻為 \(0\)​,同理,如果 \(p\)​ 存在一個大小 \(\ge 3\)​ 的置換環,那麼由於圖是一個森林,不可能這些點之間兩兩相鄰的點都存在邊,對 \(\det A\)​ 的貢獻也為 \(0\)

​。也就是說只有 \(p\)​ 中所有置換環大小都為二,也就是我們選出的是一個形如的 \((i,p_i)\)​ 的完美匹配,而由於圖是一個森林,因此完美匹配個數肯定 \(\le 1\),具體構造方案就是從葉子開始貪心地匹配,如果匹配不了就不存在完美匹配,因此 \(\det A\) 非零的充要條件是圖存在完美匹配,因此我們可以得到這樣一個性質:​

Observation. 對於圖 \(G\) 而言,\(G\) 鄰接矩陣的秩是 \(G\) 最大匹配的大小 \(\times 2\)

有了這個性質之後就可以 DP 了。\(way_{i,0/1}\) 表示欽定以 \(i\) 為根的子樹內的邊,並且在最大匹配中 \(i\)

選/沒選的方案數,\(dp_{i,0/i}\) 表示欽定以 \(i\) 為根的子樹內的邊,並且在最大匹配中 \(i\) 選/沒選的所有方案中最大匹配個數的總和。轉移就分 \((i,j)\) 的邊選/不選,然後分類討論一下即可,複雜度 \(\mathcal O(n)\)

const int MAXN=5e5;
const int MOD=998244353;
int n,hd[MAXN+5],to[MAXN*2+5],nxt[MAXN*2+5],ec=0;
void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
int dp[MAXN+5][2],way[MAXN+5][2];
void dfs(int x,int f){
	way[x][0]=1;
	for(int e=hd[x];e;e=nxt[e]){
		int y=to[e];if(y==f) continue;dfs(y,x);
		static int tmp_dp[2]={0},tmp_way[2]={0};
		memset(tmp_dp,0,sizeof(tmp_dp));
		memset(tmp_way,0,sizeof(tmp_way));
		tmp_way[0]=(1ll*way[x][0]*(way[y][0]+way[y][1]))%MOD;
		tmp_dp[0]=(1ll*dp[x][0]*(way[y][0]+way[y][1])+1ll*(dp[y][0]+dp[y][1])*way[x][0])%MOD;
		tmp_way[1]=(1ll*way[x][1]*(way[y][0]+way[y][1])*2)%MOD;
		tmp_dp[1]=2ll*(1ll*dp[x][1]*(way[y][0]+way[y][1])+1ll*(dp[y][0]+dp[y][1])*way[x][1])%MOD;
		tmp_way[0]=(tmp_way[0]+1ll*way[x][0]*way[y][1])%MOD;
		tmp_dp[0]=(tmp_dp[0]+1ll*dp[x][0]*way[y][1]+1ll*dp[y][1]*way[x][0])%MOD;
		tmp_way[1]=(tmp_way[1]+1ll*way[x][0]*way[y][0])%MOD;
		tmp_dp[1]=(tmp_dp[1]+1ll*dp[x][0]*way[y][0]+1ll*dp[y][0]*way[x][0]+1ll*way[x][0]*way[y][0])%MOD;
		for(int i=0;i<2;i++) dp[x][i]=tmp_dp[i],way[x][i]=tmp_way[i];
	}
//	printf("%d %d %d %d %d\n",x,dp[x][0],dp[x][1],way[x][0],way[x][1]);
}
int main(){
	scanf("%d",&n);
	for(int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),adde(u,v),adde(v,u);
	dfs(1,0);printf("%d\n",2ll*(dp[1][0]+dp[1][1])%MOD);
	return 0;
}