P6902 [ICPC2014 WF]Surveillance
\(\mathcal Solution\)
给定一个长度为 \(n\) 的环,有 \(k\) 个区域被覆盖,求最小的满足环被完全覆盖的区域数量。
经典模型的高层拓展!
\(\mathcal Classic\)
一个经典模型是在序列 \(1\sim n\) 上求最小区间覆盖。
解决方法是贪心的找到每次可以选择的区间中右端点最大的去拓展即可。
\(\mathcal Harder\)
一个很优秀的拓展是,将问题变为多次询问区间 \([l,r]\) 的最小覆盖。
那么应该想到倍增这种处理问题的利器,预处理出 \(f(i,j)\) 表示点 \(i\) 开始,利用 \(2^j\) 个区间能够到达的最远点。
然后倍增的做就是了,这就可以做到 \(O((n+m)\log n)\) 解决。
\(\mathcal This\ One\)
断环成链,然后枚举 \(n\) 个长度为 \(n\) 的区间,分别做一次区间覆盖即可。
利用上面多次询问提到的方法可以做到 \(O(n\log n)\) 的优秀复杂度。
将区间变为左闭右开会减少很多套路(主要是在预处理的时候)。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1e6 + 10;
const int INF = 1 << 30;
int n, k, f[N << 1][25];
struct Node{int l, r;} a[N];
int read(){
int x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
return x * f;
}
bool cmp(Node a, Node b){return a.l < b.l;}
int Get(int l, int r){
int p = l, num = 0;
for(int i = 21; i >= 0; i --)
if(f[p][i] < r + 1 && f[p][i] > p){
num += 1 << i;
p = f[p][i];
}
if(p < r + 1){
num ++;
p = f[p][0];
}
return (p >= r + 1 ? num : INF);
}
int main(){
n = read(), k = read();
for(int i = 1; i <= k; i ++){
int l = read(), r = read();
a[i] = (Node){l, r < l ? r + n : r};
}
sort(a + 1, a + k + 1, cmp);
int p = 1, r = -1;
for(int i = 1; i <= n << 1; i ++){
while(p <= k && a[p].l <= i) r = max(r, a[p].r), p ++;
f[i][0] = r + 1;
}
for(int j = 1; j <= 21; j ++)
for(int i = 1; i <= n << 1; i ++)
f[i][j] = f[f[i][j - 1]][j - 1];
int ans = INF;
for(int r = n; r <= n << 1; r ++){
int l = r - n + 1;
ans = min(ans, Get(l, r));
}
if(ans == INF) puts("impossible");
else printf("%d\n", ans);
return 0;
}