[luogu P5029] T‘ill It‘s Over(线段树优化建边网络流)

[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,tofrc>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;
}
posted @ 2020-10-26 07:41  lahlah  阅读(32)  评论(0编辑  收藏  举报