Problem
In the mathematical discipline of graph theory, a bipartite graph is a graph whose vertices can be divided into two disjoint sets U and V (that is, U and V are each independent sets) such that every edge connects a vertex in U to one in V. Vertex sets U and V are usually called the parts of the graph. Equivalently, a bipartite graph is a graph that does not contain any odd-length cycles. A matching in a graph is a set of edges without common vertices. A perfect matching is a matching that each vertice is covered by an edge in the set.
Little Q misunderstands the definition of bipartite graph, he thinks the size of U is equal to the size of V, and for each vertex p in U, there are exactly two edges from p. Based on such weighted graph, he defines the weight of a perfect matching as the product of all the edges’ weight, and the weight of a graph is the sum of all the perfect matchings’ weight.
Please write a program to compute the weight of a weighted ”bipartite graph” made by Little Q.
Input
The first line of the input contains an integer T(1≤T≤15), denoting the number of test cases.
In each test case, there is an integer n(1≤n≤300000) in the first line, denoting the size of U. The vertex in U and V are labeled by 1,2,…,n.
For the next n lines, each line contains 4 integers
vi,1,wi,1,vi,2,wi,2
(
1≤vi,j≤n,1≤wi,j≤109
), denoting there is an edge between Ui and
Vvi,1
, weighted
wi,1
, and there is another edge between Ui and
Vvi,2
, weighted
wi,2
.
It is guaranteed that each graph has at least one perfect matchings, and there are at most one edge between every pair of vertex.
Idea
首先利用拓扑处理 V 中所有度数为 1 的点,及通过删边操作成为度数为 1 的点。这些点对最终 ans 的贡献为 唯一对应边的权值×唯一对应边的权值
在处理完成后,图中所有未匹配的 U 集合点与 V 集合点个数相同(由于题意指明至少存在一个完备匹配),记作 m 个点。则 U 集合的度数和为 2m ,相应的, V 集合的度数和也为 2m (由于 U 中剩余点每点必然存在两条边,且对应连接到剩余的 V 集合点上)。由于未匹配的 V 集合点已经不存在度数为 1 的情况(已通过拓扑删除),要使得点度合法,则每个点的度数一定也为 2 。故剩余的每个连通块一定成环。间隔取边权,每个连通块的完备匹配权值为 part[0]
part[1]
。则该连通块对 ans 的贡献为
×(part0+part1)
。
Code
#include<bits/stdc++.h>
using namespace std;
const int mod = 998244353;
const int N = 300000 + 10;
struct Edge {
int nxt, to, w;
bool mrk;
} e[N*4];
int T, n, head[N*2], cnt, deg[N*2], used[N*2];
long long part[2];
void addedge(int u, int v, int w) {
e[++cnt].nxt = head[u];
e[cnt].to = v;
e[cnt].w = w;
e[cnt].mrk = 0;
head[u] = cnt;
}
void dfs(int cur, int idx) {
used[cur] = 1;
for(int i=head[cur];i;i=e[i].nxt)
{
if(e[i].mrk) continue;
e[i].mrk = e[i^1].mrk = 1;
(part[idx] *= e[i].w) %= mod;
dfs(e[i].to, 1-idx);
}
}
int main()
{
scanf("%d", &T);
while(T-- && scanf("%d", &n)!=EOF)
{
memset(head, 0, sizeof(head));
memset(deg, 0, sizeof(deg));
memset(used, 0, sizeof(used));
cnt = 1;
for(int u=1, v, w;u<=n;u++)
for(int i=1;i<=2;i++)
{
scanf("%d %d", &v, &w);
addedge(u, v+n, w);
addedge(v+n, u, w);
deg[u]++, deg[v+n]++;
}
long long ans = 1;
queue<int> que;
for(int v=n+1;v<=n+n;v++)
if(deg[v] == 1)
que.push(v);
while(!que.empty())
{
int v = que.front();
que.pop();
used[v] = 1;
for(int i=head[v];i;i=e[i].nxt)
{
if(e[i].mrk) continue;
e[i].mrk = e[i^1].mrk = 1;
used[ e[i].to ] = 1;
(ans *= e[i].w) %= mod;
for(int j=head[ e[i].to ];j;j=e[j].nxt)
{
e[j].mrk = e[j^1].mrk = 1;
deg[e[j].to]--;
if(deg[ e[j].to ] == 1) que.push(e[j].to);
}
}
}
for(int u=1;u<=n;u++)
{
if(used[u]) continue;
part[0] = part[1] = 1;
dfs(u, 0);
(ans *= (part[0]+part[1]) % mod) %= mod;
}
printf("%lld\n", ans);
}
}