[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(树链剖分)