2017多校四 1007题 hdu 6073 Matching In Multiplication 拓扑排序 + 找环

本文介绍了一种求解特定图结构中所有完美匹配权重之和的高效算法。该算法首先去除度数为1的点简化问题,再利用拓扑排序处理剩余节点,最终在环形连通块上计算两种匹配方案的权重乘积之和。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接


题意:

给定两个点集 U 与 V,U 中的每个顶点与 V 中的两个顶点相连,保证图中一定有完美匹配。

对于每个完美匹配,其 weight 为每条匹配边的权值乘积。求该图中所有完美匹配的 weight 之和。


参考:

http://blog.youkuaiyun.com/mr__kid/article/details/76684660  ——Mr__Kid

这一篇的分析写得很详细在理...。


官方题解:

首先如果一个点的度数为11,那么它的匹配方案是固定的,继而我们可以去掉这一对点。通过拓扑我们可以不断去掉所有度数为11的点。

那么剩下的图中左右各有mm个点,每个点度数都不小于22,且左边每个点度数都是22,而右侧总度数是2m2m,因此右侧只能是每个点度数都是22。这说明这个图每个连通块是个环,在环上间隔着取即可,一共两种方案。

时间复杂度O(n)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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值