[luogu P5029] T‘ill It‘s Over(线段树优化建边网络流)
题解
这题思路还是比较清楚,就是直接连边跑最大流就好了
但是主要的问题是边数太多了
这时候可以考虑线段树优化建边
线段树优化建边只是借用了一下线段树的结构罢了
左边表示的是入边(流入的),右边是出边(流出的)
图中每条边的流量都是INF
假设要
[
1
,
2
]
−
−
−
>
[
3
,
4
]
[1,2] --->[3,4]
[1,2]−−−>[3,4]
先新建两个节点
f
r
,
t
o
,
f
r
−
−
c
−
>
t
o
fr, to, fr--^{\large c}->to
fr,to,fr−−c−>to
然后
[
1
,
2
]
[1,2]
[1,2]出边连向
f
r
fr
fr,
t
o
to
to连向
[
3
,
4
]
[3,4]
[3,4]的入边
然后就没了
可以看代码理解一下
code:
#include<bits/stdc++.h>
#define N 4000005
#define INF 233333333
using namespace std;
struct edge {
int v, nxt, c;
} e[N << 1];
int p[N], eid, pp[N];
void init() {
memset(p, -1, sizeof p);
eid = 0;
}
void insert(int u, int v, int c) {
e[eid].v = v;
e[eid].c = c;
e[eid].nxt = p[u];
p[u] = eid ++;
}
void add(int u, int v, int c) { //printf("%d ---> %d %d\n", u, v, c);
insert(u, v, c);
insert(v, u, 0);
}
int S, T, n, m, k, tot, ch[N][2];
int d[N];
queue<int> q;
int bfs() {
for(int i = 0; i <= tot; i ++) pp[i] = p[i], d[i] = -1;
d[S] = 0;
q.push(S);
while(q.size()) {
int u = q.front(); q.pop();
for(int i = p[u]; i + 1; i = e[i].nxt) {
int v = e[i].v;
if(e[i].c && d[v] == -1) {
d[v] = d[u] + 1;
q.push(v);
}
}
}
return d[T] != -1;
}
int dfs(int u, int flow) {
if(u == T) return flow;
int ret = 0;
for(int i = p[u]; i + 1; i = e[i].nxt) {
int v = e[i].v;
if(e[i].c && d[v] == d[u] + 1) {
int tmp = dfs(v, min(flow, e[i].c));
e[i].c -= tmp, e[i ^ 1].c += tmp;
flow -= tmp, ret += tmp;
if(!flow) break;
}
}
if(! ret) d[u] = -1;
return ret;
}
int Dinic() {
int ret = 0;
for(; bfs() ;) ret += dfs(S, INF);//, printf("*");
return ret;
}
void build(int &rt, int &rtt, int l, int r) {
rtt = rt = ++ tot;
if(l != r) rtt = ++ tot;
//printf(" %d %d %d %d\n", l, r, rt, rtt);
if(l == r) {
if(l == 1) add(S, rt, n);
if(l == k) add(rt, T, n);
return ;
}
int mid = (l + r) >> 1;
build(ch[rt][0], ch[rtt][0], l, mid),
build(ch[rt][1], ch[rtt][1], mid + 1, r);
add(rt, ch[rt][0], INF), add(rt, ch[rt][1], INF);
add(ch[rtt][0], rtt, INF), add(ch[rtt][1], rtt, INF);
}
void link(int rt, int l, int r, int L, int R, int x, int o) {
// printf("%d %d %d %d %d %d %d\n", rt, l, r, L, R, x, o);
if(!rt) return;
if(L <= l && r <= R) {
if(o) add(x, rt, INF);
else add(rt, x, INF);
return ;
}
int mid = (l + r) >> 1;
if(L <= mid) link(ch[rt][0], l, mid, L, R, x, o);
if(R > mid) link(ch[rt][1], mid + 1, r, L, R, x, o);
}
int main() {
init();
S = ++ tot, T = ++ tot;
scanf("%d%d%d", &n, &m, &k);
int rt1 = 0, rt2 = 0;
build(rt1, rt2, 1, k);
while(m --) {
int opt, lim, l, r, ll, rr;
scanf("%d%d", &opt, &lim);
int fr = ++ tot, to = ++ tot;
add(fr, to, lim);
if(opt == 1) {
scanf("%d%d", &l, &ll);
r = l, rr = ll;
}
if(opt == 2) {
scanf("%d%d%d", &l, &r, &ll);
rr = ll;
}
if(opt == 3) {
scanf("%d%d%d", &l, &ll, &rr);
r = l;
}
if(opt == 4) {
scanf("%d%d%d%d", &l, &r, &ll, &rr);
}
link(rt2, 1, k, l, r, fr, 0);
link(rt1, 1, k, ll, rr, to, 1);
}
printf("%d", Dinic());
return 0;
}