提高模拟赛Day8T2最大匹配

提高模拟赛Day8T2最大匹配

题目

\(n\)个点\(n-1\)条边的连通图,求最大匹配及最大匹配数量.

image-20211108102802629

思路

基础的树形DP.

题目相当于从树上选出最多边,使得边没有公共点,求边数及方案数.

\(f_{i,0/1}\)表示在以\(i\)为根的子树中,\(i\)有(1)/没有(0)连边的 最大的边的数量.

\[f_{i,0}=\sum_{j\in S_i}\max(f_{j,0},f_{j,1})\\ f_{i,1}=1 + \max_{j\in S_i}\Big( f_{j,0}+\sum_{k\in S_i,k\neq 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\n" , max(f[1][0] , f[1][1]) );
    else {
        printf("%d %lld\n" , 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;
}

posted @ 2021-11-08 10:43  追梦人1024  阅读(22)  评论(0编辑  收藏  举报