HDU 6073 Matching In Multiplication (拓扑+DFS, 2017 Multi-Univ Training Contest 4)

29 篇文章 0 订阅
11 篇文章 0 订阅

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.

img

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 ( 1vi,jn,1wi,j109 ), 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);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值