题意:给出一个“二分图”,左边顶点的数目和右边相同,求这个二分图所有完美匹配的权值和。题目保证至少有一组完美匹配。
分析:如果图中存在度数为1的点,那么与这个点匹配的点就是确定的。用拓扑排序去除这些点之后,图中留下的一定是一个环,那么每隔一条边取一条就能构成完美匹配。
#include<cstdio>
#include<vector>
#include<queue>
#include<cstring>
#define mo 998244353
using namespace std;
const int maxn = 1e6;
typedef unsigned long long ll;
vector<ll> u[maxn];
vector<ll> p[maxn];
int d[maxn];
int vis[maxn];
ll n, m, k, p1, p2;
void dfs(int k, int cnt, int fa){
for(int i = 0; i < u[k].size(); i++){
if(!vis[u[k][i]] && u[k][i] != fa){
vis[u[k][i]] = 1;
if(cnt & 1) (p1 *= p[k][i]) %= mo;
else (p2 *= p[k][i]) %= mo;
dfs(u[k][i], cnt+1, k);
}
}
}
int main(){
// freopen("1007.in","r",stdin);
// freopen("data1.out","w",stdout);
int T;
scanf("%d", &T);
while(T--){
memset(vis, 0, sizeof(vis));
memset(d, 0, sizeof(d));
long long ans = 1;
scanf("%llu", &n);
for(int i = 1; i <= 2*n; i++) {
u[i].clear();
p[i].clear();
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= 2; j++){
ll x, w;
scanf("%llu%llu", &x, &w);
d[x + n]++;
u[i].push_back(x + n);
p[i].push_back(w);
u[x + n].push_back(i);
p[x + n].push_back(w);
}
d[i] = 2;
}
for(int i = n + 1; i <= 2 * n; i++){
if(u[i].size() == 1){
vis[i] = 1, d[i] = 0;
queue<int> q;
q.push(i);
while(!q.empty()){
int t = q.front();
q.pop();
for(int j = 0; j < u[t].size(); j++){
if(vis[u[t][j]] != 1){
d[u[t][j]]--;
if(t > n) (ans *= p[t][j]) %= mo;
if(d[u[t][j]] == 1){
vis[u[t][j]] = 1;
q.push(u[t][j]);
}
}
}
}
}
}
for(int i = 1; i <= n; i++){
if(!vis[i]){
p1 = p2 = 1;
dfs(i, 1, i);
ans = ans * ((p1 + p2) % mo) % mo;
}
}
printf("%llu\n", ans);
}
return 0;
}