标签:using link can print inf work 节点 pre fine
考虑查询一个区间,遍历到的叶子一定是右儿子、右儿子、……、左儿子、左儿子。
反过来,值域连续的这样的若干节点也唯一对应一个区间。
所以右儿子给值域相邻的右、左儿子连边,左儿子只给左儿子连边,问题相当于最小路径覆盖(每个点可以被覆盖无限次,有些至少一次)。
拆点上下界最小流即可。
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\n", 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\n"); 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】奇怪的线段树(广义线段树性质+上下界最小流)
标签:using link can print inf work 节点 pre fine
原文地址:https://www.cnblogs.com/coldchair/p/13399363.html