hdu 6073 Matching In Multiplication [dfs]

9 篇文章 0 订阅
3 篇文章 0 订阅

题意:给你2*n个点,左边的n个点都与右边的点连两条边,问所有完全匹配的方案中所有边权相乘之后的和。(不存在重复边)

题解:由于每个左端点只提供两条边,假如一个右端点的度为1的时候,说明这个右端点的匹配是确定的,然后将这个匹配点的连边删除,不断判断是否有度为1的点。最后剩下的点,必定都是度为2的点。剩下的图为多个环的图,环之间的匹配方案相互独立,且只有两种方案,所以根据母函数的定义,总的和为(w1+w2)*(w3+w4)······(w为一个环中的一种方案数)。

AC代码:

#include<stdio.h>
#include<vector>
#include<string.h>
#include<queue>
#include<iostream>
#include<map>
#define N 600005
#define mod 998244353
using namespace std;
typedef long long ll;
struct node
{
    ll v,w;
    node(){}
    node(ll v,ll w)
    {
        this->v=v;
        this->w=w;
    }
};
struct edge
{
	ll to,w,next;
	edge(){}
	edge(int to,int w,int next)
	{
		this->to=to;
		this->w=w;
		this->next=next;
	}
}ed[N*2];
int head[N],lnum;
void addline(int a,int b,int w)
{
	ed[lnum]=edge(b,w,head[a]);
	head[a]=lnum++;
}
ll ans,n;
ll mark[N];
ll du[N];
ll flag=0;
void dfs(ll pos,ll sum1,ll sum2,ll from,ll ff)
{
    if(flag==1)return ;
    ll f=0;
    for(ll i=head[pos];~i;i=ed[i].next)
    {
    	ll to=ed[i].to;
   		if(mark[to]==1||to==from)continue;
    	f=1;
    	mark[to]=1;
    	if(!ff)dfs(to,sum1*ed[i].w%mod,sum2,pos,ff^1);
    	else dfs(to,sum1,sum2*ed[i].w%mod,pos,ff^1);
    	if(flag==1)return ;
    }
    if(f==0)
   	{
	   	ans=ans*(sum1+sum2)%mod;
   		flag=1;
	}
}
int main()
{
    ll T;
    scanf("%lld",&T);
    while(T--)
    {
        ans=1;
        memset(du,0,sizeof(du));
        memset(mark,0,sizeof(mark));
        memset(head,-1,sizeof(head));
        lnum=0;
        scanf("%lld",&n);
        for(ll i=1;i<=n;i++)
        {
            ll a,w1,b,w2;
            scanf("%lld%lld%lld%lld",&a,&w1,&b,&w2);
            addline(i,a+n,w1%mod);
            addline(a+n,i,w1%mod);
            addline(i,b+n,w2%mod);
            addline(b+n,i,w2%mod);
            du[i]++;du[a+n]++;
            du[i]++;du[b+n]++;
        }
        queue<ll>que;
        for(ll i=1;i<=2*n;i++)
            if(du[i]==1)
        		que.push(i);
        while(!que.empty())
        {
            ll k=que.front();
            que.pop();
            ll to;
            for(int i=head[k];~i;i=ed[i].next)
            	if(mark[ed[i].to]==0)
				{
					ans=(ans*ed[i].w)%mod;
					to=ed[i].to;
					break;
				}
            mark[k]=mark[to]=1;
            for(ll i=head[to];~i;i=ed[i].next)
            {
            	ll y=ed[i].to;
            	if(mark[y]==1)continue;
            	du[y]--;
            	if(du[y]==1)
					que.push(y);
            }
        }
        for(ll i=1;i<=2*n;i++)
        {
            flag=0;
            if(mark[i]==0)
            {
                dfs(i,1,1,-1,0);
                mark[i]=1;
            }
        }
        printf("%lld\n",ans);
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值