日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

『树上匹配 树形dp』

發布時間:2025/7/25 编程问答 154 豆豆
生活随笔 收集整理的這篇文章主要介紹了 『树上匹配 树形dp』 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.


樹上匹配

Description

懶惰的溫溫今天上班也在偷懶。盯著窗外發呆的溫溫發現,透過窗戶正巧能看到一棵 n 個節點的樹。一棵 n 個節點的樹包含 n-1 條邊,且 n 個節點是聯通的。樹上兩點之間的距 離即兩點之間的最短路徑包含的邊數。
突發奇想的溫溫想要選擇一個樹上的邊集(可以為空)刪除, 使得刪除后剩下的圖的 最大匹配是唯一的。溫溫想要知道滿足條件的邊集的數量。滿足條件的邊集數量可能很多, 請對 998244353 取模。
圖的一個匹配是圖的一個邊子集,滿足條件任意兩條邊都不依附于同一個節點。圖的所 有匹配中,邊數最多的匹配即為圖的最大匹配。

Input Format

第一行一個整數 n。
接下來 n-1 行每行兩個整數 ai, bi,表示節點 ai 和 bi 之間存在一條邊。
2 ≤ n ≤ 3000 for 40%
2 ≤ n ≤ 300000 for 100%

Output Format

輸出一個整數,表示所求的滿足條件的邊集數量,對 998244353 取模

Sample Input

4 1 2 1 3 1 4

Sample Output

4

解析

先轉換題意:最大匹配唯一其實等價于圖中的一個點要么孤立,要么屬于最大匹配。

這個條件的必要性是顯然的,也就是說,最大匹配唯一,該條件一定滿足。同樣我們也可以證明該條件的充分性:反證法,假設存在一個點,它既不孤立也不屬于最大匹配,并且這個圖的最大匹配唯一。我們分兩種情況討論:

\(1.\) 這個點連向的點屬于最大匹配,那就與最大匹配的唯一性矛盾
\(2.\) 這個點連向的點不屬于最大匹配,那就與最大匹配的極大性矛盾

所以這兩個命題是等價的。

那么我們就可以考慮樹形\(dp\)計數。設\(f1[x]\)代表節點\(x\)等待他的父節點與他匹配,子樹\(x\)的方案數,\(f2[x]\)代表節點\(x\)孤立,子樹\(x\)的方案數,\(f3[x]\)代表節點\(x\)已經與某一個兒子匹配,子樹的\(x\)的方案數。然后列狀態轉移方程:

\[f1_{x}=\prod_{y\in son(x)}(f2_y+2f3_y)\\f2_x=\prod_{y\in son(x)}(f2_y+f3_y)\\f3_x=\sum_{y\in son(x)} \left ( \frac{\prod_{z\in son(x),z\not= y}(f2_z+2f3_z)}{f2_y+2f3_y}*f1_y \right )\]

為什么\(f3\)都要乘\(2\),是因為當前節點\(x\)\(f3\)這種完成狀態之間的邊可連可不連。關于第三個狀態轉移方程,其含義為選一個點\(y\)\(f1\)狀態與其匹配,剩下的同理隨便選。

\(Code:\)

#include <bits/stdc++.h> using namespace std; const int N = 300020; const long long Mod = 998244353; struct edge { int ver,next; } e[N*2]; int n,t,Head[N]; long long f1[N],f2[N],f3[N]; // f1 means which node is waiting mathcing // f2 means which node is isolated // f3 means which node has already matched inline void insert(int x,int y) {e[++t] = (edge){y,Head[x]} , Head[x] = t;e[++t] = (edge){x,Head[y]} , Head[y] = t; } inline void input(void) {scanf("%d",&n);for ( int i = 1; i < n; i++ ){int x,y;scanf("%d%d",&x,&y);insert( x , y );} } inline long long power(long long a,long long b) {long long res = 1;while ( b ){if ( 1 & b ) res = res * a % Mod;a = a * a % Mod , b >>= 1;}return res; } inline void mul(long long &a,long long b) { a = a * b % Mod; } inline void add(long long &a,long long b) { a += b; if ( a >= Mod ) a -= Mod; } inline void dp(int x,int f) {f1[x] = f2[x] = 1LL;long long val = 1LL;for ( int i = Head[x]; i; i = e[i].next ){int y = e[i].ver;if ( y == f ) continue;dp( y , x );mul( f1[x] , ( f2[y] + 2 * f3[y] ) % Mod );mul( f2[x] , ( f2[y] + f3[y] ) % Mod );mul( val , ( f2[y] + 2 * f3[y] ) % Mod );}for ( int i = Head[x]; i; i = e[i].next ){int y = e[i].ver;if ( y == f ) continue;long long inv = power( f2[y] + 2 * f3[y] , Mod-2 );long long sum = val;mul( sum , inv ) , mul( sum , f1[y] );add( f3[x] , sum );} } int main(void) {input();dp( 1 , 0 );printf("%lld\n", ( f2[1] + f3[1] ) % Mod );return 0; }

轉載于:https://www.cnblogs.com/Parsnip/p/11203680.html

總結

以上是生活随笔為你收集整理的『树上匹配 树形dp』的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。