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

题目

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

分析

fi,0|1 表示,第i个节点,选了或不选的最大匹配,
设j为i的儿子,

fi,0=jmax(fj,0,fj,1)

设k也是i的儿子,而j不包含k,
fi,1=max(fk,0+1+max(fj,0,fj,1)) ki

接着考虑最大匹配数量,即方案数,
gi,0|1 对于f这个状态最大匹配的方案数
recj=gj,0 (fj,0>fj,1)gj,1 (fj,0<fj,1)gj,0+gj,1 (fj,0=fj,1)

gi,0=Πrecj

设k也是i的儿子,而j不包含k,并且 fk,0+1+max(fj,0,fj,1) 为最大值(或之一)
gi,1=(gk,0Πrecj)

这个可以用逆元或者前缀积后缀积来处理。
发现其实 fj,0<=fj,1
所以 max(fj,0,fj,1)=fj,1

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <queue>
const long long maxlongint=2147483647;
const long long mo=1000000007;
const long long N=100005;
using namespace std;
long long n,ans,t,p,rec[N],next[N*2],last[N*2],to[N*2],tot,f[N][2],g[N][2];
int bj(long long x,long long y)
{
    next[++tot]=last[x];
    last[x]=tot;
    to[tot]=y;
}
long long mi(long long x,long long y)
{
    long long sum=1;
    while(y)
    {
        if(y&1) sum=sum*x%mo;
        x=x*x%mo;
        y/=2;
    }
    return sum; 
}
int dg(long long x,long long fa)
{
    g[x][0]=g[x][1]=1;
    f[x][0]=f[x][1]=rec[x]=0;
    long long sumf=0,mir=1,q=1;
    for(int i=last[x];i;i=next[i])
    {
        int j=to[i];
        if(j!=fa)
        {
            dg(j,x);
            sumf+=f[j][1];
            f[x][0]+=f[j][1];
            g[x][0]=g[x][0]*rec[j]%mo;
            mir=mir*rec[j]%mo;
            q=false;
        }
    }
    if(q) g[x][1]=0;
    for(int i=last[x];i;i=next[i])
    {
        int j=to[i];
        if(j!=fa)
        {
            if(f[x][1]<f[j][0]+1+sumf-f[j][1])
            {
                f[x][1]=f[j][0]+1+sumf-f[j][1];
                g[x][1]=mir*mi(rec[j],mo-2)%mo*g[j][0]%mo;
            }
            else 
            if(f[x][1]==f[j][0]+1+sumf-f[j][1])
            {
                g[x][1]=(g[x][1]+mir*mi(rec[j],mo-2)%mo*g[j][0]%mo)%mo;
            }
        }
    }
    if(f[x][0]>f[x][1]) rec[x]=g[x][0];
    else
    if(f[x][0]<f[x][1]) rec[x]=g[x][1];
    else rec[x]=g[x][0]+g[x][1];
}
int main()
{
    scanf("%lld%lld",&t,&p);
    while(t--)
    {
        tot=0;
        memset(last,0,sizeof(last));
        memset(next,0,sizeof(next));
        scanf("%lld",&n);
        for(long long i=1;i<=n-1;i++)
        {
            long long x,y;
            scanf("%lld%lld",&x,&y);
            bj(x,y);
            bj(y,x);
        }
        dg(1,0);
        printf("%lld ",max(f[1][0],f[1][1]));
        if(p==2) printf("%lld",rec[1]%mo);
        cout<<endl;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值