UOJ#217. UNR #1奇怪的线段树(广义线段树性质+上下界最小流)

Posted coldchair

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UOJ#217. UNR #1奇怪的线段树(广义线段树性质+上下界最小流)相关的知识,希望对你有一定的参考价值。

http://uoj.ac/problem/217

题解:

考虑查询一个区间,遍历到的叶子一定是右儿子、右儿子、……、左儿子、左儿子。

反过来,值域连续的这样的若干节点也唯一对应一个区间。

所以右儿子给值域相邻的右、左儿子连边,左儿子只给左儿子连边,问题相当于最小路径覆盖(每个点可以被覆盖无限次,有些至少一次)。

拆点上下界最小流即可。

Code:

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
using namespace std;

const int N = 8005;

struct P {
	int l, r, x, y, m, fa;
	int w;
} t[N]; int t0;

int n, rt;

void dg(int &i, int x, int y) {
	i = ++ t0;
	t[i].x = x; t[i].y = y;
	if(x == y) {
		scanf("%d", &t[i].w);
		return;
	}
	scanf("%d %d", &t[i].w, &t[i].m);
	dg(t[i].l, x, t[i].m);
	dg(t[i].r, t[i].m + 1, y);
	t[t[i].l].fa = t[t[i].r].fa = i;
}

int d[N], d0, id[N];

namespace gra {
	const int N = 1e6 + 5;
	
	int fi[N], to[N], nt[N], r[N], tot = 1;
	
	void link(int x, int y, int z) {
		nt[++ tot] = fi[x], to[tot] = y, r[tot] = z, fi[x] = tot;
		nt[++ tot] = fi[y], to[tot] = x, r[tot] = 0, fi[y] = tot;
	}
	
	int S, T, SS, TT;
	
	int p[N];
	
	void link(int x, int y, int l, int r) {
		p[x] -= l, p[y] += l;
		link(x, y, r - l);
	}
	
	const int inf = 1e9;
	
	int sump = 0;
	
	void rlink() {
		fo(i, 1, TT) {
			if(p[i] > 0) {
				sump += p[i];
				link(SS, i, p[i]);
			}
			if(p[i] < 0) {
				link(i, TT, -p[i]);
			}
		}
		link(T, S, inf);
	}
	
	int w[N][2], w0;
	
	int cur[N];
	
	namespace sub {
		int S, T;
		int d[N], d0, dis[N];
		
		int bfs() {
			fo(i, 1, max(S, T)) {
				dis[i] = inf;
				cur[i] = fi[i];
			}
			
			dis[S] = 0, d[d0 = 1] = S;
			for(int i = 1; i <= d0; i ++) {
				int x = d[i];
				for(int j = fi[x]; j; j = nt[j]) if(r[j]) {
					int y = to[j];
					if(dis[y] == inf) dis[y] = dis[x] + 1, d[++ d0] = y;
				}
			}
			return dis[T] < inf;
		}
		
		int dg(int x, int flow) {
			if(x == T) return flow;
			int use = 0;
			for(int &i = cur[x]; i; i = nt[i]) if(r[i] && dis[x] + 1 == dis[to[i]]) {
				int t = dg(to[i], min(flow - use, r[i]));
				r[i] -= t, r[i ^ 1] += t, use += t;
				if(use == flow) return use;
			}
			return use;
		}
		
		int work() {
			int ans = 0;
			while(bfs()) {
				ans += dg(S, inf);
			}
			return ans; 
		}
	}
	
	int lx[N];
	
	void work() {
		w0 = n;
		fo(i, 1, t0) if(t[i].w) {
			w[i][0] = ++ w0;
			w[i][1] = ++ w0;
			lx[i] = t[t[i].fa].r == i;
		}
		fo(i, 1, t0) if(t[i].w) {
			if(id[i]) {
				link(w[i][0], w[i][1], 1, inf);
			} else {
				link(w[i][0], w[i][1], 0, inf);
			}
			int k = lx[i];
			if(k == 1) {
				fo(j, 1, t0) if(t[j].w && t[j].y == t[i].x - 1 && lx[j])
					link(w[j][1], w[i][0], 0, inf);
				if(t[i].y < n) {
					link(w[i][1], t[i].y + 1, 0, inf);
				}
			} else {
				link(t[i].x, w[i][0], 0, inf);
				if(t[i].y < n) {
					link(w[i][1], t[i].y + 1, 0, inf);
				}
			}
		}
		
		S = w0 + 1, T = S + 1;
		SS = T + 1, TT = SS + 1;
		
		fo(i, 1, t0) if(t[i].w) {
			link(S, w[i][0], 0, inf);
			link(w[i][1], T, 0, inf);
		}
		rlink();
		
		sub :: S = SS; sub :: T = TT;
		int flow = sub :: work();
		int ans = r[fi[S]];
		
		cur[SS] = cur[TT] = 0;
		
		sub :: S = T; sub :: T = S;
		fi[S] = nt[fi[S]]; fi[T] = nt[fi[T]];
		
		ans -= sub :: work();
		
		pp("%d
", ans);
	}
}

int main() {
	scanf("%d", &n);
	dg(rt, 1, n);
	fo(i, 1, t0) if(t[i].w && t[i].fa && !t[t[i].fa].w) {
		pp("OwO
"); return 0;
	}
	fo(i, 1, t0) if(t[i].w) {
		if(t[t[i].l].w || t[t[i].r].w) continue;
		d[++ d0] = i;
		id[i] = d0;
	}
	gra :: work();
}

以上是关于UOJ#217. UNR #1奇怪的线段树(广义线段树性质+上下界最小流)的主要内容,如果未能解决你的问题,请参考以下文章

UOJ UNR #1火车管理 可持久化线段树

uoj#388. UNR #3配对树(线段树合并)

UOJ388 [UNR #3] 配对树

UNR #1奇怪的线段树

[UOJ388]UNR #3配对树

UOJ#308UNR#2UOJ拯救计划