题意:
给定两个点集 U 与 V,U 中的每个顶点与 V 中的两个顶点相连,保证图中一定有完美匹配。
对于每个完美匹配,其 weight 为每条匹配边的权值乘积。求该图中所有完美匹配的 weight 之和。
参考:
http://blog.youkuaiyun.com/mr__kid/article/details/76684660 ——Mr__Kid
这一篇的分析写得很详细在理...。
官方题解:
首先如果一个点的度数为1,那么它的匹配方案是固定的,继而我们可以去掉这一对点。通过拓扑我们可以不断去掉所有度数为1的点。
那么剩下的图中左右各有m个点,每个点度数都不小于2,且左边每个点度数都是2,而右侧总度数是2m,因此右侧只能是每个点度数都是2。这说明这个图每个连通块是个环,在环上间隔着取即可,一共两种方案。
时间复杂度O(n)。
Code:#include <bits/stdc++.h>
#define mod 998244353
#define maxn 600010
typedef long long LL;
using namespace std;
queue<int> q;
struct Edge {
int to, ne; LL dist;
Edge(int a = 0, LL b = 0, int c = 0) : to(a), dist(b), ne(c) {}
}edge[maxn * 4];
bool vis[maxn];
int ne[maxn], d[maxn], tot;
LL val[maxn];
void add(int u, int v, LL d) {
edge[tot] = Edge(v, d, ne[u]);
ne[u] = tot++;
}
void dfs(int u, int rt) {
vis[u] = true;
if (u == rt) return;
for (int i = ne[u]; i != -1; i = edge[i].ne) {
Edge e = edge[i]; int v = e.to;
if (vis[v]) continue;
val[++tot] = e.dist;
dfs(v, rt);
}
}
void work() {
memset(vis, 0, sizeof(vis));
memset(d, 0, sizeof(d));
int n;
scanf("%d", &n);
memset(ne, -1, sizeof(ne)); tot = 0;
for (int i = 1; i <= n; ++i) {
int v1, v2; LL w1, w2;
scanf("%d%lld%d%lld", &v1, &w1, &v2, &w2);
add(i, n + v1, w1); add(n + v1, i, w1);
add(i, n + v2, w2); add(n + v2, i, w2);
++d[n + v1]; ++d[n + v2];
}
while (!q.empty()) q.pop();
for (int i = n + 1; i <= (n << 1); ++i) if (d[i] == 1) q.push(i);
LL ans = 1;
while (!q.empty()) {
int u = q.front(); vis[u] = true;
// printf("%d\n", u);
for (int i = ne[u]; i != -1; i = edge[i].ne) {
if (vis[edge[i].to]) continue;
Edge e = edge[i]; int v = e.to;
vis[v] = true;
ans *= e.dist; ans %= mod;
for (int i = ne[v]; i != -1; i = edge[i].ne) {
Edge e = edge[i]; int vv = e.to;
if (vv == u) continue;
--d[vv]; if (d[vv] == 1) q.push(vv);
}
}
q.pop();
}
// printf("%lld\n", ans);
for (int i = 1; i <= (n << 1); ++i) {
if (vis[i]) continue;
tot = 0;
Edge e;
val[++tot] = (e = edge[ne[i]]).dist;
dfs(e.to, i);
LL sum1 = 1, sum2 = 1;
for (int j = 1; j <= tot; ++j) {
if (j & 1) sum1 *= val[j], sum1 %= mod;
else sum2 *= val[j], sum2 %= mod;
}
ans *= (sum1 + sum2) % mod; ans %= mod;
}
printf("%lld\n", ans);
}
int main() {
int T;
scanf("%d", &T);
while (T--) work();
return 0;
}