2017多校一 1009题 hdu 6041 I Curse Myself 找环(tarjan) + K 路归并

34 篇文章 0 订阅
11 篇文章 0 订阅

题目链接


题意:

给定一个仙人掌图,要求对它的最小的 K 个最小生成树求,  (k=1KkV(k))mod232 .V(k) 即为第 k 小生成树的权值和。


官方题解:

由于图是一个仙人掌,所以显然对于图上的每一个环都需要从环上取出一条边删掉。所以问题就变为有 MM 个集合,每个集合里面都有一堆数字,要从每个集合中选择一个恰好一个数加起来。求所有的这样的和中,前 KK 大的是哪些。这就是一个经典问题了。


至于 K 路归并的方法:

http://blog.csdn.net/v5zsq/article/details/76768985  ——ZSQ 

这篇里面讲得很清楚


Code:

#include <bits/stdc++.h>
#define maxm 2010
#define maxn 1010
typedef long long LL;
int kas, ne[maxn], tot, n, m, k, dfn[maxn], low[maxn], cnt, sum;
unsigned ans;
bool in[maxn];
using namespace std;
vector<int> cur, vec, vnew;
struct qnode {
    int le, ri, val;
    qnode(int a = 0, int b = 0, int c = 0) : le(a), ri(b), val(c) {}
    bool operator < (const qnode& nd) const {
        return val < nd.val;
    }
};
priority_queue<qnode> q;
stack<int> s;
inline int min(int a, int b) { return a < b ? a : b; }
struct Edge {
    int from, to, d, ne;
    Edge(int b = 0, int a = 0, int dist = 0, int c = 0) : from(b), to(a), d(dist), ne(c) {}
}edge[maxm * 2];
void add(int u, int v, int d) {
    edge[tot] = Edge(u, v, d, ne[u]);
    ne[u] = tot++;
}
bool cmp(int a, int b) { return a > b; }
void merge(vector<int>& v) {
    sort(v.begin(), v.end(), cmp);
//    for (auto x : v) printf("%d ", x); printf("\n");
    if (cur.empty()) {
        int sz = min(k, v.size());
        for (int i = 0; i < sz; ++i) {
            cur.push_back(v[i]);
        }
        return;
    }
    vnew.clear();
    while (!q.empty()) q.pop();
    int sz = min(k, v.size()), a0 = cur[0];
    for (int i = 0; i < sz; ++i) q.push(qnode(0, i, a0 + v[i]));
    int no = 0;
    while (true) {
        qnode nd = q.top(); q.pop(); ++no;
        vnew.push_back(nd.val);
        if (nd.le + 1 != cur.size()) q.push(qnode(nd.le + 1, nd.ri, cur[nd.le + 1] + v[nd.ri]));
        if (no == k || q.empty()) break;
    }
    cur = vnew;
}
void tarjan(int u, int fa) {
    dfn[u] = low[u] = ++cnt;
    for (int i = ne[u]; i != -1; i = edge[i].ne) {
        Edge e = edge[i]; int v = e.to;
        if (v == fa) continue;
        if (!dfn[v]) {
            s.push(i);
            tarjan(v, u);
            low[u] = min(low[u], low[v]);
            if (low[v] >= dfn[u]) {
                Edge e;
                vec.clear();
                while (true) {
                    int idx = s.top(); s.pop();
                    e = edge[idx];
                    vec.push_back(e.d);
                    if (idx == i) break;
                }
//                printf("%d\n", vec.size());
                if (vec.size() > 1) merge(vec);
            }
        }
        else if (dfn[v] < dfn[u] && v != fa) {
            s.push(i);
            low[u] = min(low[u], dfn[v]);
        }
    }
}
void work() {
    memset(ne, -1, sizeof(ne)); tot = 0; sum = ans = 0;
    memset(dfn, 0, sizeof(dfn)); memset(low, 0, sizeof(low));
    memset(in, 0, sizeof(in));
    cur.clear();
    for (int i = 0; i < m; ++i) {
        int x, y, z;
        scanf("%d%d%d", &x, &y, &z);
        add(x, y, z);
        add(y, x, z);
        sum += z;
    }
    scanf("%d", &k);
    for (int i = 1; i <= n; ++i) if (!dfn[i]) tarjan(i, -1);
    int sz = min(cur.size(), k);
    if (sz == 0) ans = sum;
    else for (int i = 0; i < sz; ++i) ans += (LL)(i + 1) * (sum - cur[i]);
    printf("Case #%d: %u\n", ++kas, ans);
}
int main() {
    while (scanf("%d%d", &n, &m) != EOF) work();
    return 0;
}


以及...对于 tarjan...写法还是参考的网上众题解...

想知道 dfn[v] < dfn[u] 的条件 等价于 v 点还在栈中吗...? 我是觉得因为是仙人掌图所以才可以这么说...的吧0 0 如果哪位看到这里还请麻烦告诉我 Orz

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值