[P4155][SCOI2015] 国旗计划 (贪心+倍增)

题意:给一个环和多个区间,且不存在被包含区间,求在已选定第 i个区间时( i 属于 ( 1,n ) ),总共需要多少段区间才可将整个环给覆盖完;

解法:贪心+倍增;

1.贪心;区间覆盖问题的较常见的一个解决思路就是按端点排序,如果是环的话,还要断环为链;

1.贪心;题目中说到不存在被包含区间,即不存在任意两个区间,使得其中的一个区间被另一个区间包含,那么我们可以知道不会有任意两条线段的同一边的端点重合,所以我们就可以在排完序后进行倍增预处理;设 f[i][j]为第 i条线段跳了 2^j步后可以到达的线段序号,在处理的时候,要注意一下边界;

附上代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#define ll long long
#define rg register
using namespace std;
const int N = 4e5+10;

inline int read(){
    int ref=0,x=1;char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) ref=ref*10+ch-'0',ch=getchar();
    return ref*x;
}

int n,m;
struct node{
    int l,r,id;
}a[N];
int f[N][20],rel[N>>1];

bool cmp(node a,node b){
    return a.l<b.l;
}

void prepare(){
    int limit=2*n;
    for(int i=1,pos=i;i<=limit;++i){
        while(pos<=limit&&a[pos].l<=a[i].r) pos++;
        f[i][0]=pos-1;
    }
    for(rg int j=1;j<20;++j)
    for(rg int i=1;i<=limit;++i) f[i][j]=f[f[i][j-1]][j-1];
}

void deal(int x){
    int end=a[x].l+m,ans=1,fina=x;
    for(rg int i=19;i>=0;i--){
        int pre=f[x][i];
        if(a[pre].r<end) ans+=(1<<i),x=pre;
    }
    rel[a[fina].id]=ans+1;
}

int main()
{
    n=read(),m=read();
    for(rg int i=1;i<=n;++i){
        a[i].l=read(),a[i].r=read();
        if(a[i].l>a[i].r) a[i].r+=m;
        a[i].id=i;
    }
    sort(a+1,a+n+1,cmp);
    for(rg int i=1;i<=n;++i){
        a[i+n]=a[i];
        a[i+n].l=a[i].l+m;
        a[i+n].r=a[i].r+m;
    }
    prepare();
    for(rg int i=1;i<=n;++i) deal(i);
    for(rg int i=1;i<=n;++i) printf("%d ",rel[i]);
    return 0;
}
原文地址:https://www.cnblogs.com/nnezgy/p/11545145.html