bzoj4383 [POI2015]Pustynia 拓扑排序+差分约束+线段树优化建图

题目传送门

https://lydsy.com/JudgeOnline/problem.php?id=4383

题解

暴力的做法显然是把所有的条件拆分以后暴力建一条有向边表示小于关系。

因为不存在零环,所以随后可以用拓扑排序来解决。


考虑如何优化。

第一个优化比较显而易见,对于一个条件,新建一个虚点,连向所有给出的点。

第二个是,我们可以发现,给定这 \(k\) 个点以后,实际上需要建的边是从最多 \(k + 1\) 个区间中引出的,所以可以考虑线段树优化建图。

然后就可以和上面一样的,因为不存在零环,所以随后可以用拓扑排序来解决。

然后这样的时间复杂度是 \(O((m+\sum k)\log n)\)


这似乎是我第一次写线段树优化建图呢~虽然一年前就已经学过了,但是一直没写过。

#include<bits/stdc++.h>

#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back

template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b, 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b, 1 : 0;}

typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;

template<typename I> inline void read(I &x) {
	int f = 0, c;
	while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
	x = c & 15;
	while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
	f ? x = -x : 0;
}

const int N = 100000 * 6 + 7;
const int M = 100000 * (4 + 3 + 17 + 17) + 7;

#define lc o << 1
#define rc o << 1 | 1

int n, ns, m, nod;
int dp[N], nd[N], idg[N], pre[N], rel[N], tmp[N], q[N];

struct Edge { int to, ne, w; } g[M]; int head[N], tot;
inline void addedge(int x, int y, int z) { g[++tot].to = y, g[tot].w = z, g[tot].ne = head[x], head[x] = tot; }
inline void adde(int x, int y, int z) { addedge(x, y, z), addedge(y, x, z); }

inline void add_e(int x, int y, int z) { ++idg[y], addedge(x, y, z); }

inline void build(int o, int L, int R) {
	if (L == R) return (void)(rel[L] = o, pre[o] = L);
	int M = (L + R) >> 1;
	add_e(lc, o, 0), build(lc, L, M);
	add_e(rc, o, 0), build(rc, M + 1, R);
}
inline void addi(int o, int L, int R, int l, int r, int x) {
//	dbg("o = %d, L = %d, R = %d, l = %d, r = %d, x = %d\n", o, L, R, l, r, x);
	if (l <= L && R <= r) return add_e(o, x, 1);
	int M = (L + R) >> 1;
	if (l <= M) addi(lc, L, M, l, r, x);
	if (r > M) addi(rc, M + 1, R, l, r, x);
}

inline void work() {
	int hd = 0, tl = 0;
	for (int i = 1; i <= nod; ++i) if (idg[i] == 0) q[++tl] = i, smax(dp[i], 1);
	while (hd < tl) {
		int x = q[++hd];
//		if (dp[x] >= n) dbg("******* x = %d, dp[x] = %d, nd[x] = %d\n", x, dp[x], nd[x]);
		if (nd[x]) {
			if (dp[x] > nd[x]) {
				puts("NIE");
				return;
			} else dp[x] = nd[x];
		}
		for fec(i, x, y) {
			smax(dp[y], dp[x] + g[i].w);
			if (!--idg[y]) q[++tl] = y;
		}
	}
	for (int i = 1; i <= n; ++i) if (!dp[rel[i]] || dp[rel[i]] > 1e9) {
		puts("NIE");
		return;
	}
	puts("TAK");
	for (int i = 1; i <= n; ++i) printf("%d%c", dp[rel[i]], " \n"[i == n]);
}

inline void init() {
	read(n), read(ns), read(m);
	int x, y;
	build(1, 1, n);
	nod = n << 2;
	for (int i = 1; i <= ns; ++i) read(x), read(y), dp[rel[x]] = nd[rel[x]] = y;
	for (int i = 1; i <= m; ++i) {
		int l, r, k;
		read(l), read(r), read(k);
		++nod;
		for (int i = 1; i <= k; ++i) {
			read(tmp[i]);
			add_e(nod, rel[tmp[i]], 0);
			if (i == 1) tmp[i] > l ? addi(1, 1, n, l, tmp[i] - 1, nod) : (void)0;
			else tmp[i] - tmp[i - 1] > 1 ? addi(1, 1, n, tmp[i - 1] + 1, tmp[i] - 1, nod) : (void)0;
		}
		if (tmp[k] != r) addi(1, 1, n, tmp[k] + 1, r, nod);
	}
}

int main() {
#ifdef hzhkk
	freopen("hkk.in", "r", stdin);
#endif
	init();
	work();
	fclose(stdin), fclose(stdout);
	return 0;
}
posted @ 2019-10-31 17:05  hankeke303  阅读(155)  评论(0编辑  收藏  举报