题意:每个左边的点有两条边连到右边,一个完美匹配的权重等于匹配边的价值累乘,求权重和
思路:官方题解写的挺清楚的。
官方题解:首先如果一个点的度数为1,那么它的匹配方案是固定的,继而我们可以去掉这一对点。通过拓扑我们可以不断去掉所有度数为1的点。
那么剩下的图中左右各有m个点,每个点度数都不小于2,且左边每个点度数都是2,而右侧总度数是2m,因此右侧只能是每个点度数都是2。这说明这个图每个连通块是个环,在环上间隔着取即可,一共两种方案。
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<stdlib.h>
#include<math.h>
#include<vector>
#include<list>
#include<map>
#include<stack>
#include<queue>
#include<algorithm>
#include<numeric>
#include<functional>
using namespace std;
typedef long long ll;
const int maxn = 6e5+5;
const int MOD = 998244353;
int n,d[maxn],vis[maxn];
ll tot,ans;
vector<int> v[maxn],w[maxn];
void init()
{
memset(d,0,sizeof d);
for(int i = 1; i <= 2*n; i++)
{
v[i].clear();
w[i].clear();
}
}
void add(int x,int y,int val)
{
v[x].push_back(y);
w[x].push_back(val);
d[x]++;
v[y].push_back(x);
w[y].push_back(val);
d[y]++;
}
void topsort()
{
queue<int> q;
while(!q.empty())
q.pop();
for(int i = 1; i <= 2*n; i++)
{
if(d[i] == 1)
{
d[i] = 0;
q.push(i);
}
}
while(!q.empty())
{
int x = q.front();q.pop();
int y;
for(int i = 0; i < v[x].size(); i++)
{
if(d[ v[x][i] ])
{
tot = tot * w[x][i] % MOD;
y = v[x][i];
d[y] = 0;
break;
}
}
for(int i = 0; i < v[y].size(); i++)
{
if(v[y][i] == x) continue;
int tp = v[y][i];
d[tp]--;
if(d[tp] == 1)
{
d[tp] = 0;
q.push(tp);
}
}
}
}
void dfs(int x,int t,ll a,ll b,int fa)
{
int i;
if(fa != -1)
d[x] = 0;
for(i = 0; i < v[x].size(); i++)
{
if(d[ v[x][i] ] && v[x][i] != fa)
{
if(t%2)
a = a * w[x][i] % MOD;
else
b = b * w[x][i] % MOD;
break;
}
}
if(i < v[x].size())
dfs(v[x][i],t+1,a,b,x);
else
ans = ans * ( a + b) % MOD;
}
int main(void)
{
int T,i,j,k;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
init();
for(i = 1; i <= n; i++)
{
int a,b,c,d;
scanf("%d%d%d%d",&a,&b,&c,&d);
add(i,a+n,b);
add(i,c+n,d);
}
tot = 1ll;
topsort();
ans = tot;
for(i = 1; i <= 2*n; i++)
{
if(d[i] == 0) continue;
dfs(i,1,1,1,-1);
}
printf("%lld\n",ans);
}
return 0;
}