您的位置:首页 > 其它

hdu 5452 Minimum Cut(树链剖分+差分前缀和)

2015-09-22 20:01 381 查看

题意:

给一个无向图和它的一个生成树,要求找到一个最小割,使得有且只有一条生成树上的一条边属于割集。

解析:

因为生成树中只有一条边属于割集,那么割对生成树来说只是分成了两个子树,那么就考虑割生成树上割哪条边是最优的。


首先对生成树进行建树剖,对于每条非树边的两个端点u和v,对 u –> v 在生成树上的简单路径上的边权值加一,最后找到所有边权值最小的边,就是属于最小割的边。

所有对找到的这条边的权值做贡献的边,一定是跨越了以这条边分开的两个子树,即如果要分开这两个子树,这些边也要割掉,这些边就是要求的最小割集。


这题卡常数,所以路径上边权加一的操作不能用线段树或者树状数组来更新,因为是操作完成后才对边权进行遍历,所以可以用差分前缀和来计算每条边被更新了几次。

mymy codecode

[code]#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cctype>
#include <vector>
#define pb push_back
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 20005;
const int M = 200005;

namespace IO {
    const static int maxn = 200 << 18;
    static char buf[maxn], *pbuf = buf, *End;
    void init() {
        int c = fread(buf, 1, maxn, stdin);
        End = buf + c;
    }
    int &readint() {
        static int ans;
        ans = 0;
        while (pbuf != End && !isdigit(*pbuf)) pbuf ++;
        while (pbuf != End && isdigit(*pbuf)) {
            ans = ans * 10 + *pbuf - '0';
            pbuf ++;
        }
        return ans;
    }
}

struct Query {
    int u, v;
} Q[M];

vector<int> G
;

int fa
, son
, size
, top
, deep
;
int p
, fp
, dfs_clock;

void dfs1(int u, int pre, int de) {
    deep[u] = de, fa[u] = pre, size[u] = 1;
    son[u] = 0;
    for (int i = 0; i < G[u].size(); i++) {
        int v = G[u][i];
        if (v == pre) continue;
        dfs1(v, u, de + 1);
        size[u] += size[v];
        if (size[v] > size[son[u]])
            son[u] = v;
    }
}

void dfs2(int u, int tp) {
    top[u] = tp;
    p[u] = ++dfs_clock;
    fp[dfs_clock] = u;
    if(son[u]) dfs2(son[u], tp);
    for (int i = 0; i < G[u].size(); i++) {
        int v = G[u][i];
        if (v != fa[u] && v != son[u])
            dfs2(v, v);
    }
}

void init_chain(int u) {
    dfs_clock = 0;
    dfs1(u, 0, 1);
    dfs2(u, u);
}

int n, m, q;
int sum
;

inline void modify(int ql, int qr, int val) {
    sum[ql] += val; sum[qr+1] -= val;
}

void build() {
    for(int i = 1; i <= n; i++)
        sum[i] += sum[i-1];
}

void update(int u, int v, int val) {
    while (top[u] != top[v]) {
        if (deep[top[u]] < deep[top[v]])
            swap(u, v);
        modify(p[top[u]], p[u], val);
        u = fa[top[u]];
    }
    if (deep[u] < deep[v])  swap(u, v);
    modify(p[v], p[u], val);
}

inline void addEdge(int u, int v) {
    G[u].pb(v);
}

void init() {
    q = 0;
    memset(sum, 0, sizeof(sum));
    for(int i = 1; i <= n; i++) G[i].clear();
}

int main() {
    IO::init();
    int u, v;
    int T, cas = 1;
    T = IO::readint();
    while(T--) {
        init();
        n = IO::readint();
        m = IO::readint();
        for(int i = 1; i <= m; i++) {
            u = IO::readint();
            v = IO::readint();
            if(i < n) {
                addEdge(u, v);
                addEdge(v, u);
            }else
                Q[q++] = (Query){u, v};
        }
        init_chain(1);
        for(int i = 0; i < q; i++) {
            u = Q[i].u, v = Q[i].v;
            update(u, v, 1);
        }
        build();
        int ans = INF;
        for(int i = 2; i <= n; i++)
            ans = min(ans, sum[i]+1);
        printf("Case #%d: %d\n", cas++, ans);
    }
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: