您的位置:首页 > 其它

jzoj4887 最大匹配

2016-11-26 15:39 295 查看

问题描述

现在给你一个N个点N-1条边的连通图,希望你能够求出这个图的最大匹配以及最大匹配的数量。

两个匹配不同当且仅当存在一条边在第一个匹配中存在而在第二个匹配中不存在。

若p=1,则一行一个数输出图的最大匹配

若p=2,则一行两个数输出图的最大匹配以及最大匹配数量,答案对10^9+7取模。



比赛时过了p=1+暴力的60分

首先不难求出最大匹配.

设Gi,0/1分别为这个点有没有被占用的最大匹配.

其中j是i的子节点

G[i,0]=∑max(G[j,1],G[j,0]).

G[i,1]=max(G[j,0](i与j这条边选入)+∑max(G[j′,1],G[j′,0])(其他照常))+1

然后max(g[1,0],g[1,1])就是第一问的答案.

对于第二问,我们设Fi,0/1分别为当这个点被占用/没被占用,且最大匹配等于对应的G的时候的方案数.

再用y(j)表示节点i对应选最大的方案数,要是Gi,0大那么就是Fi,0,要是Gi,1大那么就是Fi,1,若相等则为二者之和.

F[i,0]=∏y(j)

其中z是能够使得G[i,1]最大的j的集合中的任意一个.

因为我们要保证他的匹配必须是最大的.局部最大才能推出全局最大.

F[i,1]=∑(F[z,0](这条边入选)∗∏y(j)(其他照常))

这样话,y(1)即为答案.

打的时候错误比较多,因为方程有点诡异..

#include <cstdio>
#include <iostream>
#include <cstring>
#define maxn 100010
#define mo 1000000007
#define max(a,b) ((a)>(b)?(a):(b))
#define inver(x) (qf(x,mo-2))
using namespace std;
typedef long long ll;
ll from[maxn*2],to[maxn*2],next[maxn*2],head[maxn],tot,g[maxn][2],f[maxn][2],vis[maxn];
ll t,n,p,ans2,ans1;
ll qf(ll x,ll y) {
if (y==0) return 1;
if (y==1) return x;
ll tmp=qf(x,y>>1);
return tmp*tmp%mo*qf(x,y&1)%mo;
}
ll rec(ll tt){
if (g[tt][0]==g[tt][1]) return f[tt][0]+f[tt][1];
return (g[tt][0]>g[tt][1])?f[tt][0]:f[tt][1];
}

void dfs(ll x,ll fa) {
ll t,si=0,sum=0,ma=0,can=1;
f[x][0]=1;
for (int k=head[x]; k; k=next[k]) {
t=to[k];
if (t==fa) continue;
si=1;
dfs(t,x);
sum+=g[t][1];
can=can*rec(t)%mo;
f[x][0]=f[x][0]*rec(t)%mo;
}
if (si==0) {
g[x][0]=0,g[x][1]=0;
f[x][0]=1,f[x][1]=0;
return;
}
for (int k=head[x]; k; k=next[k]) {
t=to[k];
if (t==fa) continue;
g[x][0]+=max(g[t][1],g[t][0]);
if (sum-g[t][1]+g[t][0]>ma) ma=sum-g[t][1]+g[t][0];
}
for (int k=head[x]; k; k=next[k]) {
t=to[k];
if (t==fa) continue;
if (sum-g[t][1]+g[t][0]==ma)
f[x][1]=(f[x][1]+f[t][0]*(can*inver(rec(t))%mo)%mo)%mo;
}
g[x][1]=ma+1;
}

void link(int a,int b) {
to[++tot]=b;
from[tot]=a;
next[tot]=head[a];
head[a]=tot;
}
int main() {
freopen("hungary.in","r",stdin);
//freopen("hungary.out","w",stdout);
for (cin>>t>>p; t; t--) {
memset(g,0,sizeof g);
memset(f,0,sizeof f);
memset(next,0,sizeof next);
memset(to,0,sizeof to);
memset(head,0,sizeof head);
tot=0;
cin>>n;
int a,b;
for (int i=1; i<n; i++) {
scanf("%d %d",&a,&b);
link(a,b);
link(b,a);
}
dfs(1,0);
ans1=max(g[1][0],g[1][1]);
cout<<ans1<<" ";
if (p==2) cout<< rec(1)%mo;
cout<<endl;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: