[XSY 1129] flow 最小割 树链剖分 线段树优化建图

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[XSY 1129] flow 最小割 树链剖分 线段树优化建图相关的知识,希望对你有一定的参考价值。

题意

  给定一张 $N$ 个点的有根树, $1$ 为树的根.

  每个点有点权 $V[i]$ . 若 $V[i] > 0$ , 则可以获得 $V[i]$ 的收益; 若 $V[i] < 0$ , 则需要付出 $V[i]$ 的代价.

  如果选择了某个点 $x$ , 且它的连续的 $K$ 个祖先中存在一个没有选, 则需要付出 $P_x$ 的代价.

  最大化总收益.

 

分析

  将 树链剖分 + zkw线段树 嵌入网络中, 跑最小割.

 

实现

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#include <vector>
using namespace std;

#define F(i, a, b) for (register int i = (a); i <= (b); i++)

const int N = 20005;
const int INF = ~0u>>1;

int n, m;
struct Edge {
    int x, y, w; bool tag;
    friend inline bool operator < (Edge A, Edge B) { return A.w < B.w; }
}ed[100005];

int f[N];
int sum, srd[N];
vector<int> g[N];

int pre[N], dep[N], siz[N], son[N];
int top[N], tid[N], pid[N], num;

vector<Edge> cross[N];
int mn[70000], out[N];    // int m;
int id[N];

inline int rd(void) {
    int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == -) f = -1;
    int x = 0; for (; isdigit(c); c = getchar()) x = x*10+c-0; return x*f;
}
inline void gmin(int &x, int y) { x = min(x, y); }

inline int Find(int x) { return f[x] == x ? x : f[x] = Find(f[x]); }

void Son(int x) {
    siz[x] = 1;
    for (vector<int>::iterator it = g[x].begin(); it != g[x].end(); it++)
        if (pre[x] != *it) {
            pre[*it] = x, dep[*it] = dep[x]+1;
            Son(*it);
            siz[x] += siz[*it];
            if (siz[son[x]] < siz[*it]) son[x] = *it;
        }
}
void Split(int x, int anc) {
    top[x] = anc, pid[ tid[x] = ++num ] = x;
    if (son[x] > 0) Split(son[x], anc);
    for (vector<int>::iterator it = g[x].begin(); it != g[x].end(); it++)
        if (pre[x] != *it && *it != son[x])
            Split(*it, *it);
}

inline int LCA(int x, int y) {
    while (top[x] != top[y])
        dep[top[x]] > dep[top[y]] ? x = pre[top[x]] : y = pre[top[y]];
    return dep[x] < dep[y] ? x : y;
}

inline void Min(int L, int R, int w) {
    for (L--, R++, L += (1<<m), R += (1<<m); L^R^1; L >>= 1, R >>= 1) {
        if (!(L&1)) gmin(mn[L^1], w);
        if (R&1) gmin(mn[R^1], w);
    }
}
inline void Fill(int x, int D, int w) {
    // dep >= D
    if (dep[x] < D) return;
    for (int t = top[x]; ; x = pre[t], t = top[x])
        if (dep[t] > D) Min(tid[t], tid[x], w);
        else {
            Min(tid[x] + (D - dep[x]), tid[x], w);
            //        ?        D
            //      tid[x]   dep[x]
            break;
        }
}
void Tsunami(int x, int L, int R, int w) {
    if (R < 1 || L > n) return;
    gmin(w, mn[x]);
    if (L == R) { out[pid[L]] = w; return; }
    int M = (L+R)>>1;
    Tsunami(x<<1, L, M, w);
    Tsunami(x<<1|1, M+1, R, w);
}

inline int Go(int x, int D) {
    // guaranteed that dep[x] >= D
    for (int t = top[x]; ; x = pre[t], t = top[x])
        if (D >= dep[t]) return pid[tid[x] + (D - dep[x])];    //同上
}

int main(void) {
    #ifndef ONLINE_JUDGE
        freopen("xsy2441.in", "r", stdin);
        freopen("xsy2441.out", "w", stdout);
    #endif

    for (int T = rd(), t = 1; t <= T; t++) {
        n = rd(), m = rd();
        F(i, 1, m) {
            int x = rd(), y = rd(), d = rd(), c = rd();
            ed[i] = (Edge){x, y, d * (1 - c), false};
        }

        sort(ed+1, ed+m+1);
        F(i, 1, n) f[i] = i, g[i].clear(); sum = 0, memset(srd, 0, sizeof srd);
        int c = 1;
        for (int i = 1; i <= m && c < n; i++) {
            int x = ed[i].x, y = ed[i].y, w = ed[i].w;
            int fx = Find(ed[i].x);
            int fy = Find(ed[i].y);
            if (fx != fy) {
                ed[i].tag = true, g[x].push_back(y), g[y].push_back(x);
                f[fx] = fy;
                sum += w, srd[x] += w, srd[y] += w, c++;
            }
        }

        memset(pre, 0, sizeof pre), memset(dep, 0, sizeof dep), memset(siz, 0, sizeof siz), memset(son, 0, sizeof son);
        memset(top, 0, sizeof top), memset(tid, 0, sizeof tid), memset(pid, 0, sizeof pid), num = 0;
        Son(1);
        Split(1, 1);

        F(i, 1, n) cross[i].clear();
        F(i, 1, m) if (!ed[i].tag) {
            int x = ed[i].x, y = ed[i].y, z = LCA(x, y);
            if (x != z && y != z)
                cross[z].push_back(ed[i]);
        }

        int tmp = m;
        for (m = 0; (1<<m) - 1 < n+1; m++);    //reuse m
        fill(mn+1, mn+(1<<m)+(1<<m), INF), fill(out+1, out+n+1, INF);
        F(i, 1, tmp) if (!ed[i].tag) {
            int x = ed[i].x, y = ed[i].y, w = ed[i].w, z = LCA(x, y);
            Fill(x, dep[z]+2, w);
            Fill(y, dep[z]+2, w);
        }
        Tsunami(1, 0, (1<<m)-1, INF);

        memset(id, 0, sizeof id);
        F(cut, 1, n) {
            int cnt = 0;
            for (vector<int>::iterator it = g[cut].begin(); it != g[cut].end(); it++)
                if (*it != pre[cut])
                    id[*it] = ++cnt;
            if (pre[cut] > 0) cnt++;
            num = 0;        // num, edge[] are reused.

            for (vector<Edge>::iterator it = cross[cut].begin(); it != cross[cut].end(); it++) {
                int x = it->x, y = it->y, w = it->w;
                int son_x = Go(x, dep[cut]+1);
                int son_y = Go(y, dep[cut]+1);
                ed[++num] = (Edge){id[son_x], id[son_y], w, false};
            }

            if (pre[cut] > 0)
                for (vector<int>::iterator it = g[cut].begin(); it != g[cut].end(); it++)
                    if (*it != pre[cut] && out[*it] != INF)
                        ed[++num] = (Edge){cnt, id[*it], out[*it], false};

            sort(ed+1, ed+num+1);
            F(i, 1, cnt) f[i] = i;
            int c = 1, s = 0;
            for (int i = 1; i <= num && c < cnt; i++) {
                int x = ed[i].x, y = ed[i].y, w = ed[i].w;
                int fx = Find(x);
                int fy = Find(y);
                if (fx != fy) {
                    f[fx] = fy;
                    s += w, c++;
                }
            }
            if (c < cnt) puts("inf"); else printf("%d\n", sum - srd[cut] + s);
        }
    }

    return 0;
}

 

以上是关于[XSY 1129] flow 最小割 树链剖分 线段树优化建图的主要内容,如果未能解决你的问题,请参考以下文章

[USACO15DEC]最大流Max Flow(树链剖分,线段树)

P3128 [USACO15DEC]Max Flow P(树链剖分)

UVA 11354 - Bond (最小生成树 + 树链剖分)

树链剖分算法解析

树链剖分—学习笔记

[HDU 3710] Battle over Cities 树链剖分 最小生成树