题目
(n)个点(n-1)条边的连通图,求最大匹配及最大匹配数量.
思路
基础的树形DP.
题目相当于从树上选出最多边,使得边没有公共点,求边数及方案数.
设(f_{i,0/1})表示在以(i)为根的子树中,(i)有(1)/没有(0)连边的 最大的边的数量.
则
[f_{i,0}=sum_{jin S_i}max(f_{j,0},f_{j,1})\
f_{i,1}=1 + max_{jin S_i}Big(
f_{j,0}+sum_{kin S_i,k
eq j}max(f_{k,0},f_{k,1})
Big)
]
其中(S_i)表示(i)的子节点的集合.
设(g_{i,0/1})表示方案数,转移方程略.
代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int read() {
int re = 0;
char c = getchar();
bool negt = false;
while(c < '0' || c > '9')negt |= (c == '-') , c = getchar();
while(c >= '0' && c <= '9')re = (re << 1) + (re << 3) + c - '0' , c = getchar();
return negt ? -re : re;
}
typedef long long ll;
const int N = 1e5 + 10;
ll mod = 1e9 + 7;
namespace Graph {
struct Edge {
int to , nxt;
} ed[N * 2];
int head[N];
int cnt;
void addedge(int u , int v) {
++cnt;
ed[cnt].to = v , ed[cnt].nxt = head[u] , head[u] = cnt;
}
void initialize() {
memset(ed , 0 , sizeof(ed));
memset(head , 0 , sizeof(head));
cnt = 0;
}
} // namespace Graph
using Graph::ed;
using Graph::head;
using Graph::addedge;
int OUTPUT_TYPE;
int n;
int f[N][2];
ll g[N][2];
ll inv(ll mul) {//逆元
ll res = 1;
int p = mod - 2;
while(p) {
if(p & 1)res = res * mul % mod;
mul = mul * mul % mod;
p >>= 1;
}
return res;
}
ll contri[N];
void dfs(int x , int fa) {
for(int i = head[x] ; i ; i = ed[i].nxt) {
int to = ed[i].to;
if(to == fa)continue;
dfs(to , x);
f[x][0] += max(f[to][0] , f[to][1]);
}
for(int i = head[x] ; i ; i = ed[i].nxt) {
int to = ed[i].to;
if(to == fa)continue;
f[x][1] = max(f[x][1] , f[x][0] - max(f[to][0] , f[to][1]) + f[to][0] + 1 );
g[x][1] = g[x][1] * g[to][0] % mod;
}
if(OUTPUT_TYPE == 2) {
ll sum = 1;
for(int i = head[x] ; i ; i = ed[i].nxt) {
int to = ed[i].to;
if(to == fa)continue;
if(f[to][0] == f[to][1]) contri[to] = (g[to][0] + g[to][1]) % mod;
else contri[to] = (f[to][0] > f[to][1] ? g[to][0] : g[to][1]) % mod;
sum = sum * contri[to] % mod;
}
g[x][0] = sum;
for(int i = head[x] ; i ; i = ed[i].nxt) {
int to = ed[i].to;
if(to == fa)continue;
if(f[x][1] == f[x][0] - max(f[to][0] , f[to][1]) + f[to][0] + 1 )
g[x][1] += sum * inv(contri[to]) % mod * g[to][0] % mod , g[x][1] %= mod;
}
}
}
void solve() {
Graph::initialize();
memset(f , 0 , sizeof(f));
memset(g , 0 , sizeof(g));
memset(contri , 0 , sizeof(contri));
n = read();
for(int i = 1 ; i < n ; i++) {
int u = read() , v = read();
addedge(u , v) , addedge(v , u);
}
dfs(1 , 0);
if(OUTPUT_TYPE == 1)printf("%d
" , max(f[1][0] , f[1][1]) );
else {
printf("%d %lld
" , max(f[1][0] , f[1][1]) , (f[1][0] == f[1][1] ? g[1][0] + g[1][1] : (f[1][0] > f[1][1] ? g[1][0] : g[1][1])) % mod );
}
}
int main() {
freopen("hungary.in" , "r" , stdin);
freopen("hungary.out" , "w" , stdout);
int T = read();
OUTPUT_TYPE = read();
while(T--)solve();
return 0;
}