题意:给你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);
}
}