【NOIP2016提高A组集训第13场11.11】最大匹配
题目
mhy12345学习了二分图匹配,二分图是一种特殊的图,其中的点可以分到两个集合中,使得相同的集合中的点两两没有连边。
图的“匹配”是指这个图的一个边集,里面的边两两不存在公共端点。
匹配的大小是指该匹配有多少条边。
二分图匹配我们可以通过匈牙利算法得以在O(VE)时间复杂度内解决。
mhy12345觉得单纯的二分图匹配算法毫无难度,因此提出新的问题:
现在给你一个N个点N-1条边的连通图,希望你能够求出这个图的最大匹配以及最大匹配的数量。
两个匹配不同当且仅当存在一条边在第一个匹配中存在而在第二个匹配中不存在。
分析
设\(f_{i,0|1}\)表示,第i个节点,选了或不选的最大匹配,
设j为i的儿子,
\[f_{i,0}=\sum_jmax(f_{j,0},f_{j,1})
\]
设k也是i的儿子,而j不包含k,
\[f_{i,1}=max(f_{k,0}+1+\sum max(f_{j,0},f_{j,1}))\ 将k与i匹配,剩下的取最大
\]
接着考虑最大匹配数量,即方案数,
设\(g_{i,0|1}\)对于f这个状态最大匹配的方案数
设$$rec_{j}=\left{\begin{array}\g_{j,0}\ (f_{j,0}>f_{j,1})\g_{j,1}\ (f_{j,0}<f_{j,1})\g_{j,0}+g_{j,1}\ (f_{j,0}=f_{j,1})\end{array}\right.$$
\[g_{i,0}=\Pi rec_j
\]
设k也是i的儿子,而j不包含k,并且\(f_{k,0}+1+\sum max(f_{j,0},f_{j,1})\)为最大值(或之一)
\[g_{i,1}=\sum(g_{k,0}·\Pi rec_j)
\]
这个可以用逆元或者前缀积后缀积来处理。
发现其实\(f_{j,0}<=f_{j,1}\),
所以\(max(f_{j,0},f_{j,1})=f_{j,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;
}
}