【题解】 UVA1707 【Surveillance】

看到是环,于是我们非常套路地把它拉成链。

接着就要枚举一个起点 i i i,找到至少覆盖到 i + l e n − 1 i+len-1 i+len1这个位置需要使用的最少线段数。

由于每个点都要计算,所以可以考虑预处理倍增,计算 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,j1,j1

p i , 0 p_{i,0} pi,0即为覆盖 i i i点的线段的最大右端点,可以用差分的思想,配合一个set维护。

接着枚举每一个点,在不超过 i + l e n − 1 i+len-1 i+len1的范围内尽量跳(类似求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;
}

猜你喜欢

转载自blog.csdn.net/qq_30115697/article/details/109486163