题目
mhy12345学习了二分图匹配,二分图是一种特殊的图,其中的点可以分到两个集合中,使得相同的集合中的点两两没有连边。
图的“匹配”是指这个图的一个边集,里面的边两两不存在公共端点。
匹配的大小是指该匹配有多少条边。
二分图匹配我们可以通过匈牙利算法得以在O(VE)时间复杂度内解决。
mhy12345觉得单纯的二分图匹配算法毫无难度,因此提出新的问题:
现在给你一个N个点N-1条边的连通图,希望你能够求出这个图的最大匹配以及最大匹配的数量。
两个匹配不同当且仅当存在一条边在第一个匹配中存在而在第二个匹配中不存在。
题解
题目给的是一棵树,如果考虑最大匹配就只需要看它与它儿子的匹配情况。
设
fi,0/1
表示第i个节点匹配了或者没匹配的情况下,最大匹配数。
转移比较容易推:
fx,0=∑ymax(fy,0,fy,1)(y∈sonx)
我们可以知道
fx,0≤fx,1
所以,转移式就应该变为:
fx,0=∑yfy,1(y∈sonx)
如果x别匹配了,那么在它的儿子中就一定要有一个儿子跟它匹配。
所以
fx,1=max(fk,0+∑ymax(fy,0,fy,1)(y∈sonx,y≠k)
其中k是x的儿子,k与x匹配。
对于方案数,我们再设一个rec,
recx
就表示x的方案数,
用
gi,0/1
表示第i个节点匹配了或者没匹配的情况下,最大匹配数的方案数。
reci
的取值:
fi,0/1
中哪个大取哪个
gi
,一样大就
gi,0/1
加起来。
gx,0=
全部
recson
乘起来。
gx,1=gk,0∗
剩下的rec。
k是
fx,1
最大的时候的k。
code(c++)
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define N 100003
#define _ %1000000007
using namespace std;
int next[2*N],a[2*N],b[N];
long long f[N][2],g[N][2],rec[N];
int n,t,p,tot,x,y;
long long ksm(long long x,int y)
{
long long s=1;
while(y)
{
if(y%2)s=s*x _;
x=x*x _;
y/=2;
}
return s;
}
int dg(int x,int fa)
{
g[x][0]=g[x][1]=1;
f[x][0]=f[x][1]=rec[x]=0;
long long sum=0,mx=1;
bool pd=1;
for(int i=b[x];i;i=next[i])
if(a[i]!=fa)
{
dg(a[i],x);
sum=(sum+f[a[i]][1])_;
f[x][0]+=f[a[i]][1];
g[x][0]=g[x][0]*rec[a[i]]_;
mx=mx*rec[a[i]]_;
pd=0;
}
if(pd) g[x][1]=0;
for(int i=b[x];i;i=next[i])
if(a[i]!=fa)
{
if(f[x][1]<f[a[i]][0]+1+sum-f[a[i]][1])
{
f[x][1]=f[a[i]][0]+1+sum-f[a[i]][1];
g[x][1]=mx*ksm(rec[a[i]],1000000005)_*g[a[i]][0]_;
}
else
if(f[x][1]==f[a[i]][0]+1+sum-f[a[i]][1])g[x][1]=(g[x][1]+mx*ksm(rec[a[i]],1000000005)_*g[a[i]][0]_)_;
}
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()
{
freopen("hungary.in","r",stdin);
freopen("hungary.out","w",stdout);
scanf("%d%d",&t,&p);
while(t--)
{
memset(b,0,sizeof(b));
memset(f,0,sizeof(g));
memset(g,0,sizeof(g));
memset(next,0,sizeof(next));
memset(a,0,sizeof(a));
tot=0;
scanf("%d",&n);
for(int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
tot++;
a[tot]=y;
next[tot]=b[x];
b[x]=tot;
tot++;
a[tot]=x;
next[tot]=b[y];
b[y]=tot;
}
dg(1,0);
if(f[1][0]>f[1][1])y=0;else y=1;
printf("%lld ",f[1][y]);
if(p==2)printf("%lld",rec[1]);
printf("\n");
}
}