HDU 6073 Matching In Multiplication

44 篇文章 0 订阅
32 篇文章 0 订阅

2017多校4-7 Matching In Multiplication

图论,思维

题意

给你一个二分图,左右各n个点,左边每个点都连出两条边。保证至少有一个完美匹配,对于一个完美匹配,价值是边权之积,要求所有完美匹配的价值和。

思路

首先如果一个点的度数为1,那么它的匹配方案是固定的,继而我们可以去掉这一对点。通过拓扑我们可以不断去掉所有度数为1的点。

那么剩下的图中左右各有m个点,每个点度数都不小于2,且左边每个点度数都是2,而右侧总度数是2m,因此右侧只能是每个点度数都是2。这说明这个图每个连通块是个环,在环上间隔着取即可,一共两种方案。

时间复杂度O(n)。

代码

#include<bits\stdc++.h>
#define M(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long LL;
const int MAXN=600007;
const int mod=998244353;
int indeg[MAXN];
struct Edge
{
    int to;int co;bool fl;int ne;
}e[2*MAXN];
int edge=0;
int head[MAXN];
void addedge(int u, int v, int c)
{
    e[edge].to=v, e[edge].co=c, e[edge].fl=0;
    e[edge].ne=head[u];head[u]=edge;
    edge++;
}
int del[MAXN];
queue<int> que;
LL ans1=1, ans2=1;
void dfs(int now, int fl)
{
    del[now]=1;
    for(int i=head[now];~i;i=e[i].ne)
    {
        int to=e[i].to;int co=e[i].co;
        if(e[i].fl) continue;
        e[i].fl=1, e[i^1].fl=1;
        if(fl) ans1=ans1*co%mod;
        else ans2=ans2*co%mod;
        dfs(to, fl^1);
    }
}
int main()
{
    int T;scanf("%d", &T);
    while(T--)
    {
        while(!que.empty()) que.pop();
        M(indeg, 0);M(del, 0);M(head, -1);
        edge=0;
        int n;scanf("%d", &n);
        for(int i=1;i<=n;i++)
        {
            int a, b, c, d;scanf("%d%d%d%d", &a, &b, &c, &d);
            addedge(i, a+n, b);
            addedge(a+n, i, b);
            indeg[i]++, indeg[a+n]++;
            addedge(i, c+n, d);
            addedge(c+n, i, b);
            indeg[i]++, indeg[c+n]++;
        }
        //LL tmp=1;
        for(int i=n+1;i<=2*n;i++)
        {
            if(indeg[i]==1)
            {
                que.push(i);
            }
        }
        LL ans=1;
        while(!que.empty())
        {
            int now=que.front();que.pop();
            del[now]=1;
            for(int i=head[now];~i;i=e[i].ne)
            {
                if(e[i].fl) continue;
                e[i].fl=1;e[i^1].fl=1;
                int to=e[i].to;
                del[to]=1;
                ans=(ans*e[i].co)%mod;
                for(int j=head[to];~j;j=e[j].ne)
                {
                    if(e[j].fl) continue;
                    e[j].fl=1;e[j^1].fl=1;
                    indeg[e[j].to]--;
                    if(indeg[e[j].to]==1) que.push(e[j].to);
                }
            }
        }
        for(int i=1;i<=2*n;i++)
        {
            if(!del[i])
            {
                ans1=ans2=1;
                dfs(i, 0);
                ans=ans*((ans1+ans2)%mod)%mod;
            }
        }
        printf("%lld\n", ans);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值