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;
}