bzoj 4082 [Wf2014]Surveillance 倍增

题面

题目传送门

解法

不妨将环变成链

那么就变成选出若干段区间,使得它们中间没有空隙且长度≥len

将所有区间按照右端点排序

那么,对于每一个区间,我们都可以找到右端点最大的且能和该区间连接的区间

然后发现这些关系形成了一棵树

那么,现在我们枚举最左边的区间,倍增找到最近的且能完全覆盖这个环的区间

原题中在无解的情况下需要输出impossible,然而题目并没有显示

时间复杂度:\(O(n\ log\ n)\)

代码

#include <bits/stdc++.h>
#define N 1000010
using namespace std;
template <typename node> void chkmax(node &x, node y) {x = max(x, y);}
template <typename node> void chkmin(node &x, node y) {x = min(x, y);}
template <typename node> void read(node &x) {
	x = 0; int f = 1; char c = getchar();
	while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
struct Node {
	int l, r;
	bool operator < (const Node &a) const {
		return r < a.r;
	}
} a[N];
int d[N], mx[N], f[N][21];
void getd(int x) {
	if (!f[x][0]) d[x] = 1;
	if (d[x]) return;
	getd(f[x][0]); d[x] = d[f[x][0]] + 1;
}
int main() {
	int n, m; read(m), read(n);
	for (int i = 1; i <= n; i++) {
		read(a[i].l), read(a[i].r);
		if (a[i].l > a[i].r) a[i].r += m;
	}
	sort(a + 1, a + n + 1);
	for (int i = 1; i <= n; i++) chkmax(mx[a[i].l], i);
	int tmp = 0;
	for (int i = 1, j = 1; i <= n; i++) {
		for (; j <= a[i].r + 1; j++)
			chkmax(tmp, mx[j]);
		f[i][0] = (tmp == i) ? 0 : tmp;
	}
	for (int i = 1; i <= n; i++) getd(i);
	for (int j = 1; j <= 20; j++)
		for (int i = 1; i <= n; i++)
			f[i][j] = f[f[i][j - 1]][j - 1];
	int ans = 1 << 30;
	for (int i = 1; i <= n; i++) {
		int x = i;
		for (int j = 20; j >= 0; j--)
			if (a[f[x][j]].r - a[i].l + 1 < m && f[x][j]) x = f[x][j];
		if (a[x].r - a[i].l + 1 < m) x = f[x][0];
		if (a[x].r - a[i].l + 1 >= m) chkmin(ans, d[i] - d[x] + 1);
	}
	if (ans == (1 << 30)) cout << "impossible\n";
		else cout << ans << "\n";
	return 0;
}

posted @ 2018-08-14 18:24  谜のNOIP  阅读(221)  评论(0编辑  收藏  举报