hdu6041

hdu6041

题意

给出一个仙人掌

如果一个无向连通图的任意一条边最多属于一个简单环,我们就称之为仙人掌。所谓简单环即不经过重复的结点的环。

求前 (K) 小生成树 。

分析

仙人掌中每个环中我们最多可以删掉一条边,题目就变成了有 (M) 个数组,每次从每个数组中分别取一个数字并求和,前 (K) 大的和。

首先用 (Tarjan) 算法找环,然后使用优先队列不断合并两个数组( 多路归并问题,见白书 P189 )。

code

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int MAXN = 1e5 + 10;
const int M = 2e3;
int n, m, K;
struct Edge {
    int to, w, next;  
}e[M << 1];
int cnt, head[M << 1];
void addedge(int u, int v, int w) {
    e[cnt].to = v;
    e[cnt].w = w;
    e[cnt].next = head[u];
    head[u] = cnt++;
}
int a[MAXN], b[MAXN], c[MAXN];
int dfn[M], sz;
stack<int> sta;
struct Item {
    int s;
    int x;
    Item(int s, int x):s(s), x(x) {}
    bool operator<(const Item& other) const {
        return s < other.s;
    }
};
void cal() {
    priority_queue<Item> q;
    for(int i = 1; i <= b[0]; i++) {
        q.push(Item(a[1] + b[i], 2));
    }
    c[0] = 0;
    while(c[0] < K && !q.empty()) {
        Item it = q.top(); q.pop();
        c[++c[0]] = it.s; 
        if(it.x <= a[0]) q.push(Item(it.s - a[it.x - 1] + a[it.x], it.x + 1));
    }
    a[0] = c[0];
    for(int i = 1; i <= c[0]; i++) {
        a[i] = c[i];
    }
}
int tarjan(int fa, int u) {
    dfn[u] = ++sz;
    int lowu = sz;
    for(int i = head[u]; ~i; i = e[i].next) {
        int v = e[i].to;
        if(!dfn[v]) {
            sta.push(i);
            int lowv = tarjan(u, v);
            if(lowu <= lowv) {
                b[0] = 0;
                while(1) {
                    int j = sta.top(); sta.pop();
                    b[++b[0]] = e[j].w;
                    if(j == i) break;
                }
                if(b[0] > 1) cal();
            } else lowu = lowv;
        } else if(v != fa && lowu > dfn[v]) {
            sta.push(i);
            lowu = dfn[v];
        }
    }
    return lowu;
}
int main() {
    int kase = 1;
    while(~scanf("%d%d", &n, &m)) {
        while(!sta.empty()) sta.pop();
        cnt = 0;
        sz = 0;
        memset(dfn, 0, sizeof dfn);
        memset(head, -1, sizeof head);
        a[0] = 1; a[1] = 0;
        int s = 0;
        for(int i = 0; i < m; i++) {
            int u, v, w;
            scanf("%d%d%d", &u, &v, &w);
            s += w;
            addedge(u, v, w); 
            addedge(v, u, w);
        }
        scanf("%d", &K);
        tarjan(-1, 1);
        unsigned ans = 0;
        for(int i = 1; i <= a[0]; i++) {
            ans = ans + (unsigned)i * (s - a[i]);
        }         
        if(a[0] == 0) ans = s;
        printf("Case #%d: %u
", kase++, ans);
    }   
    return 0;
}
原文地址:https://www.cnblogs.com/ftae/p/7260507.html