看到是环,于是我们非常套路地把它拉成链。
接着就要枚举一个起点 i i i,找到至少覆盖到 i + l e n − 1 i+len-1 i+len−1这个位置需要使用的最少线段数。
由于每个点都要计算,所以可以考虑预处理倍增,计算 p i , j p_{i,j} pi,j表示从 i i i开始,使用 2 j 2^j 2j条线段不能覆盖的第一个位置。转移即为 p i , j = p p i , j − 1 , j − 1 p_{i,j}=p_{p_{i,j-1},j-1} pi,j=ppi,j−1,j−1
p i , 0 p_{i,0} pi,0即为覆盖 i i i点的线段的最大右端点,可以用差分的思想,配合一个set
维护。
接着枚举每一个点,在不超过 i + l e n − 1 i+len-1 i+len−1的范围内尽量跳(类似求LCA的过程),最后判断一下能否完全覆盖即可。
代码
#include <bits/stdc++.h>
#define MAX 2000005
#define INF 0x3f3f3f3f
using namespace std;
struct node{
int l, r;
}a[MAX];
int m, n;
int p[MAX][24];
vector<int> v[MAX];
multiset<int> s;
int main()
{
while(cin >> m >> n){
memset(p, 0, sizeof(p));
for(int i = 1; i <= m*2; i++) v[i].clear();
for(int i = 1; i <= n; i++){
scanf("%d%d", &a[i].l, &a[i].r);
if(a[i].r < a[i].l) a[i].r += m;
v[a[i].l].push_back(a[i].r);
v[a[i].r+1].push_back(-a[i].r);
}
s.clear();
for(int i = 1; i <= m*2; i++){
for(int x:v[i]){
if(x > 0) s.insert(x);
else s.erase(s.find(-x));
}
if(!s.empty()) p[i][0] = *s.rbegin()+1;
else p[i][0] = i;
}
for(int j = 1; j <= 21; j++){
for(int i = 1; i <= m*2; i++){
p[i][j] = p[p[i][j-1]][j-1];
}
}
int ans = INF;
for(int i = 1; i <= m; i++){
int x = i, t = 0;
for(int j = 21; j >= 0; j--){
if(p[x][j] <= i+m-1) x = p[x][j], t += (1<<j);
}
if(p[x][0] > i+m-1) ans = min(ans, t+1);
}
if(ans == INF) puts("impossible");
else cout<<ans<<endl;
}
return 0;
}