【JZOJ4887】【NOIP2016提高A组集训第13场11.11】最大匹配

题目描述

mhy12345学习了二分图匹配,二分图是一种特殊的图,其中的点可以分到两个集合中,使得相同的集合中的点两两没有连边。
图的“匹配”是指这个图的一个边集,里面的边两两不存在公共端点。
匹配的大小是指该匹配有多少条边。
二分图匹配我们可以通过匈牙利算法得以在O(VE)时间复杂度内解决。
mhy12345觉得单纯的二分图匹配算法毫无难度,因此提出新的问题:
现在给你一个N个点N-1条边的连通图,希望你能够求出这个图的最大匹配以及最大匹配的数量。
两个匹配不同当且仅当存在一条边在第一个匹配中存在而在第二个匹配中不存在。

数据范围

这里写图片描述

分析与演绎

演绎直接得出树形动态规划。
设f[i]表示取i的最大匹配数,F[i]为这个情况下的方案数;
g[i]表示不取i的最大匹配数,G[i]为这个情况下的方案数。
转移方程显然。

代码

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#define write(x) (cout<<(x))
#define writeln(x) (cout<<(x)<<endl)
#define ll long long
using namespace std;
const char* fin="hungary.in";
const char* fout="hungary.out";
const ll inf=0x7fffffff;
const ll maxn=100008,maxm=maxn*2,mo=1000000007;
ll t,m,n,i,j,k;
ll f[maxn],F[maxn],g[maxn],G[maxn],h[maxn],H[maxn];
ll fi[maxn],la[maxm],ne[maxm],tot=0;
void add_line(ll a,ll b){
    tot++;
    ne[tot]=fi[a];
    la[tot]=b;
    fi[a]=tot;
}
ll read(){
    ll x=0;
    char ch=getchar();
    while (ch<'0' && ch>'9') ch=getchar();
    while (ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
    return x;
}
ll qpower(ll a,ll b){
    ll c=1;
    while (b){
        if (b&1) c=c*a%mo;
        a=a*a%mo;
        b>>=1;
    }
    return c;
}
ll N(ll a){
    return qpower(a,mo-2);
}
void dfs(ll v,ll from){
    ll i,j=0,k,sum=0,times=1;
    f[v]=0;
    F[v]=0;
    g[v]=0;
    G[v]=1;
    for (k=fi[v];k;k=ne[k])
        if (la[k]!=from){
            dfs(la[k],v);
            g[v]+=h[la[k]];
            G[v]=G[v]*H[la[k]]%mo;
            sum+=h[la[k]];
            times=times*H[la[k]]%mo;
            j=1;
        }
    for (k=fi[v];k;k=ne[k]){
        if (la[k]!=from){
            if (f[v]<sum-h[la[k]]+g[la[k]]+1){
                f[v]=sum-h[la[k]]+g[la[k]]+1;
                F[v]=times*N(H[la[k]])%mo*G[la[k]]%mo;
            }else if (f[v]==sum-h[la[k]]+g[la[k]]+1) F[v]=(F[v]+times*N(H[la[k]])%mo*G[la[k]]%mo)%mo;
        }
    }
    if (f[v]>g[v]) h[v]=f[v],H[v]=F[v];
    else if (f[v]<g[v]) h[v]=g[v],H[v]=G[v];
    else h[v]=f[v],H[v]=(F[v]+G[v])%mo;
}
int main(){
    freopen(fin,"r",stdin);
    freopen(fout,"w",stdout);
    t=read();
    m=read();
    while (t--){
        tot=0;
        memset(fi,0,sizeof(fi));
        n=read();
        for (i=1;i<n;i++){
            j=read();
            k=read();
            add_line(j,k);
            add_line(k,j);
        }
        dfs(1,0);
        write(h[1]);
        if (m==2) write(" "),write(H[1]);
        write(endl);
    }
    return 0;
}

启发

写动态规划之前,一定要明确动态规划转移方程。

原文地址:https://www.cnblogs.com/hiweibolu/p/6714837.html