jzoj4887 最大匹配

39 篇文章 0 订阅
21 篇文章 0 订阅

问题描述

现在给你一个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](ij)+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;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值