HDU 6073 - Matching In Multiplication | 2017 Multi-University Training Contest 4

/*
HDU 6073 - Matching In Multiplication [ 图论 ]  |  2017 Multi-University Training Contest 4
题意:
	定义一张二分图,U中每个节点和V中两个节点连边
	完美匹配的权值为该匹配所有边的权值相乘
	求所有完美匹配的权值之和
分析:
	可以发现有些V中的点只能连唯一的U中的点
	按拓扑排序思路将这些全部处理掉后,剩下的点构成一个个环
	每个环有两种连线方式,间隔取边权
*/
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 600005;
const LL MOD = 998244353;
struct Edge {
    int v; LL w;
};
vector<Edge> G[N];
int vis[N];
int cnt[N];
int t, n;
LL ans;
queue<int> que;
LL s[2];
void dfs(int x, int pre, int p)
{
    if (vis[x]) return;
    vis[x] = 2;
    for (const auto & e : G[x])
    {
        if (vis[e.v] == 1 || e.v == pre) continue;
        s[p] = s[p] * e.w % MOD;
        dfs(e.v, x, p^1);
        break;
    }
}
void solve()
{
    for (int i = n+1; i <= 2*n; i++)
        if (G[i].size() == 1) que.push(i);
    ans = 1;
    while (!que.empty())
    {
        int y = que.front(); que.pop();
        vis[y] = 1;
        for (const auto& e: G[y])
        {
            if (!vis[e.v])
            {
                vis[e.v] = 1;
                ans = ans * e.w % MOD;
                for (const auto & ee: G[e.v])
                {
                    if (vis[ee.v]) continue;
                    cnt[ee.v]--;
                    if (cnt[ee.v] == 1) que.push(ee.v);
                }
            }
        }
    }
    for (int i = 1; i <= n; i++)
    {
        if (!vis[i])
        {
            s[0] = s[1] = 1;
            dfs(i, i, 0);
            ans = ans * (s[0] + s[1]) % MOD;
        }
    }
}
void init(int n)
{
    for (int i = 0; i <= n; i++) G[i].clear();
    while (!que.empty()) que.pop();
    memset(vis, 0, sizeof(vis));
    memset(cnt, 0, sizeof(cnt));
}
int main()
{
    scanf("%d", &t);
    while (t--)
    {
        scanf("%d", &n);
        init(n<<1);
        for (int i = 1; i <= n; i++)
        {
            int v; LL w;
            scanf("%d%lld", &v, &w); v += n;
            G[i].push_back(Edge{v, w});
            G[v].push_back(Edge{i, w});
            cnt[v]++;
            scanf("%d%lld", &v, &w); v += n;
            G[i].push_back(Edge{v, w});
            G[v].push_back(Edge{i, w});
            cnt[v]++;
        }
        solve();
        printf("%lld\n", ans);
    }
}

  

转载于:https://www.cnblogs.com/nicetomeetu/p/7293883.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值