问题描述
mhy12345学习了二分图匹配,二分图是一种特殊的图,其中的点可以分到两个集合中,使得相同的集合中的点两两没有连边。
图的“匹配”是指这个图的一个边集,里面的边两两不存在公共端点。
匹配的大小是指该匹配有多少条边。
二分图匹配我们可以通过匈牙利算法得以在O(VE)时间复杂度内解决。
mhy12345觉得单纯的二分图匹配算法毫无难度,因此提出新的问题:
现在给你一个N个点N-1条边的连通图,希望你能够求出这个图的最大匹配以及最大匹配的数量。
两个匹配不同当且仅当存在一条边在第一个匹配中存在而在第二个匹配中不存在。
输入格式
第一行两个数T,P,其中T表示数据组数。
接下来每组数据第一行一个数N
接下来N-1行每行两个数分别表示一条边。
输出格式
对于每组数据,输出一行:
若p=1,则一行一个数输出图的最大匹配
若p=2,则一行两个数输出图的最大匹配以及最大匹配数量。
输入输出样例一
hungary.in |
hungary.out |
1 1 2 1 2 |
1 |
题解:
问题可以看成层与层间、父亲和儿子间选和不选的两种情况
选一个节点可以看作选下图中的块:
约定如下:
f[i],选中编号为i的节点的最大匹配;
F[i],选中编号为i的节点的最大匹配的方案数;
g[i],不选编号为i的节点的最大匹配;
G[i],不选编号为i的节点的最大匹配的方案数;
h[i],编号为i节点的最大匹配;
H[i],编号为i节点的最大匹配的方案数;
于是初始的动规方程如下:
g[i]=g[i]+h[son]
G[i]=G[i]*H[son]
f[i]=max(f[i],(∑h[son,son])-h[son]+g[son])
F[i]=(∏H[son])/h[son]*G[son]
#include<stdio.h> #include<string.h> #define buf 100001 #define mo 1000000007 typedef long long ll; inline void S(int &x){ x=0;int c=getchar(),f=1; for(;c<48||c>57;c=getchar()) if(!(c^45)) f=-1; for(;c>47&&c<58;c=getchar()) x=(x<<1)+(x<<3)+c-48; x*=f; } int n,fst[buf],nxt[buf<<1],v[buf<<1],tot; ll g[buf],G[buf],f[buf],F[buf],h[buf],H[buf]; inline void link(int a,int b){ v[++tot]=b, nxt[tot]=fst[a], fst[a]=tot, v[++tot]=a, nxt[tot]=fst[b], fst[b]=tot; } ll P(ll a,ll b){ ll c=1; for(;b;b>>=1){ if(b&1) c=c*a%mo; a=a*a%mo; } return c; } ll N(ll a){ return P(a,mo-2); } void D(int x,int fa){ G[x]=1; g[x]=f[x]=F[x]=h[x]=H[x]=0; ll mul=1,sum=0; for(int j=fst[x];j;j=nxt[j]) if(v[j]^fa) D(v[j],x), g[x]+=h[v[j]], G[x]=G[x]*H[v[j]]%mo, sum+=h[v[j]], mul=mul*H[v[j]]%mo; for(int j=fst[x];j;j=nxt[j]) if(v[j]^fa) if(f[x]<sum-h[v[j]]+g[v[j]]+1) f[x]=sum-h[v[j]]+g[v[j]]+1, F[x]=mul*N(H[v[j]])%mo*G[v[j]]%mo; else if(!(f[x]^(sum-h[v[j]]+g[v[j]]+1))) F[x]=(F[x]+mul*N(H[v[j]])%mo*G[v[j]]%mo)%mo; if(f[x]>g[x]) h[x]=f[x], H[x]=F[x]; else if(f[x]<g[x]) h[x]=g[x], H[x]=G[x]; else h[x]=f[x], H[x]=(F[x]+G[x])%mo; } int main(){ int T,p; freopen("hungary.in","r",stdin), freopen("hungary.out","w",stdout); S(T),S(p); while(T--){ tot=0; memset(fst,0,sizeof(fst)); S(n); for(int i=1,x,y;i<n;i++) S(x), S(y), link(x,y); D(1,0); if(p&1) printf("%I64d ",h[1]); else printf("%I64d %I64d ",h[1],H[1]); } fclose(stdin), fclose(stdout); }