hdu6073 Matching In Multiplication(搜索)

题意:左右两边各有n个点,左边每个点都向右边连出两条边。求图上所有完全匹配的乘积和。
想法:
首先,最简单的情况:左右两边所有点的度数为2,那么该图只会是一个或多个环,对于每一个环,只会有两个解,所以将环中的边间隔放入两个解中,这两个解相加后乘入最终答案。

对于有度数为1的点的情况:那么该点的匹配是唯一的,那么从度数为1的点开始,拓扑删除所有可能产生或已经有的度数为1的点的匹配边,之后转为上一种情况。


#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<queue>
#include<stack>
#include<set>
#include<map>
#include<deque>
#include<vector>
#include<functional>
using namespace std;
#define LL long long
#define mm(a,b) memset(a,b,sizeof(a))
const double eps=1.0e-6;
const double PI=acos(-1.0);
template<typename T>T gcd(T a,T b){return b==0?a:gcd(b,a%b);}
template<typename T>T lcm(T a,T b){return a/gcd(a,b)*b;}
template<typename T>T _abs(T a){return a>0?a:-a;}
const int maxn=600010;
const LL mod=998244353;
struct abc
{
    int to,next,w;
    bool f;//标记是否删除
}e[maxn*4];
int cnt,head[maxn],du[maxn];
LL ji;
void add(int x,int y,int z)
{
    du[x]++;
    du[y]++;
    e[cnt].f=0;
    e[cnt].to=y;
    e[cnt].w=z;
    e[cnt].next=head[x];
    head[x]=cnt++;
    e[cnt].f=0;
    e[cnt].to=x;
    e[cnt].w=z;
    e[cnt].next=head[y];
    head[y]=cnt++;
}
void dfs(int id,int f)
{
    for(int k=head[id];k!=-1;k=e[k].next)
        if(!e[k].f)
        {
            int to=e[k].to;
            if(f)//搜索时是从一边跳向另一边,在跳回来(“左->右->左”或反过来),只有从起始的那一边(左或右)出发的边才计入答案
                ji=ji*e[k].w%mod;
            e[k].f=e[k^1].f=1;
            du[id]--;
            du[to]--;
            if(du[to]==1)
                dfs(to,f^1);
        }
}
LL ans[2];
void dfs2(int id,int now)
{
    for(int k=head[id];k!=-1;k=e[k].next)
        if(!e[k].f)
        {
            int to=e[k].to;
            ans[now]=ans[now]*e[k].w%mod;
            e[k].f=e[k^1].f=1;
            du[id]--;
            du[to]--;
            dfs2(to,now^1);
        }
}
int main()
{
    int t,n,v1,w1,v2,w2;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        cnt=0;
        ji=1;
        ans[0]=ans[1]=1;
        mm(head,-1);
        mm(du,0);
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d%d%d",&v1,&w1,&v2,&w2);
            add(i,v1+n,w1);
            add(i,v2+n,w2);
        }
        for(int i=1;i<=2*n;i++)
            if(du[i]==1)
                dfs(i,1);//删除度数为1的点的匹配边
        for(int i=1;i<=2*n;i++)
            if(du[i]>=2)//成环的情况
            {
                ans[0]=ans[1]=1;//对于每一个环,匹配情况只有两种
                dfs2(i,0);
                ji=ji*((ans[0]+ans[1])%mod)%mod;
            }
        printf("%lld\n",ji);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值