1. 程式人生 > >[SCOI2015]國旗計劃[Wf2014]Surveillance

[SCOI2015]國旗計劃[Wf2014]Surveillance

[SCOI2015]國旗計劃

A國正在開展一項偉大的計劃——國旗計劃。這項計劃的內容是邊防戰士手舉國旗環繞邊境線奔襲一圈。這 項計劃需要多名邊防戰士以接力的形式共同完成,為此,國土安全域性已經挑選了N名優秀的邊防戰上作為這 項計劃的候選人。 A國幅員遼闊,邊境線上設有M個邊防站,順時針編號1至M。每名邊防戰士常駐兩個邊防站,並且善於 在這兩個邊防站之間長途奔襲,我們稱這兩個邊防站之間的路程是這個邊防戰士的奔襲區間。n名邊防戰士 都是精心挑選的,身體素質極佳,所以每名邊防戰士的奔襲區間都不會被其他邊防戰士的奔襲區間所包含。 現在,國十安全域性局長希望知道,至少需要多少名邊防戰士,才能使得他們的奔襲區間覆蓋全部的邊境線, 從而順利地完成國旗計劃。不僅如此,安全域性局長還希望知道更詳細的資訊:對於每一名邊防戰士,在他必 須參加國旗計劃的前提下,至少需要多少名邊防戰士才能覆蓋全部邊境線,從而順利地完成國旗計劃。 題解
如果是一條鏈的情況,就可以直接貪心做了。 但這是個環,根據國際慣例,斷環為鏈。 分析一下,我們可以把一條邊拆成兩個。 對於l<r的情況、

 

對於l>r的情況。

注意,後面那一條線段不要漏掉,否則會WA一個點。

於是就可以愉快的倍增了,對於每一個點都倍增一遍。

程式碼

#include<cstdio>
#include<iostream>
#include<algorithm>
#define N 400002
using namespace std;
typedef unsigned 
int ll; ll len,b[N<<1]; int n,p[21][N],top,tag[N<<1],an[N]; inline int rd(){ int x=0;char c=getchar();bool f=0; while(!isdigit(c)){if(c=='-')f=1;c=getchar();} while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();} return f?-x:x; } struct node{ ll l,r;int id;
bool operator <(const node &b)const{return r<b.r;} }a[N]; int main(){ n=rd();len=rd();int haha=n; for(int i=1;i<=n;++i){ a[i].l=rd(),a[i].r=rd();a[i].id=i; if(a[i].r<a[i].l){ a[i].r+=len; a[++haha].l=a[i].l+len;a[haha].r=len*2;a[haha].id=haha; } else{ a[++haha].l=a[i].l+len;a[haha].r=a[i].r+len;a[haha].id=haha; b[++top]=a[i].l+len;b[++top]=a[i].r+len; } b[++top]=a[i].l;b[++top]=a[i].r; } b[++top]=len; sort(a+1,a+haha+1);sort(b+1,b+top+1); top=unique(b+1,b+top+1)-b-1; for(int i=1;i<=haha;++i){ int x=lower_bound(b+1,b+top+1,a[i].l)-b; if(a[i].r>a[tag[x]].r)tag[x]=i; } for(int i=2;i<=top;++i)if(a[tag[i-1]].r>a[tag[i]].r)tag[i]=tag[i-1]; for(int i=1;i<=haha;++i){ int x=lower_bound(b+1,b+top+1,a[i].r)-b; p[0][i]=tag[x]; } for(int i=1;i<=20;++i) for(int j=1;j<=haha;++j)p[i][j]=p[i-1][p[i-1][j]]; for(int i=1;i<=haha;++i){ if(i>n)continue; ll ans=2e9,t=a[i].l+len,num=0; int now=i; for(int j=20;j>=0;--j){ int x=p[j][now]; if(!x)continue; if(a[x].r>=t)ans=min(ans,num+(1<<j)); else now=x,num+=(1<<j); } an[a[i].id]=ans+1; } for(int i=1;i<=n;++i)printf("%u ",an[i]); return 0; }

[Wf2014]Surveillance

給你一個長度為len的環,以及n個區間,要你選擇儘量少的區間,使得它們完全覆蓋整個環。問最少要多少個區間。

和上一題基本相同,但是略有區別,這道題要求覆蓋,上一道題還要求相鄰兩條線段有交。

但是這道題我們就可以不用拆線段了,因為我們從每個點都倍增一遍一定能找到最優解。

程式碼

#include<cstdio>
#include<iostream>
#include<algorithm>
#define N 1000002
using namespace std;
typedef unsigned int ll;
ll len,b[N*3];
int n,p[22][N],top,tag[N*3],an[N];
inline int rd(){
    int x=0;char c=getchar();bool f=0;
    while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return f?-x:x;
}
struct node{
    ll l,r;int id;
    bool operator <(const node &b)const{return r<b.r;}
}a[N];
int main(){
      len=rd();    n=rd();
    for(int i=1;i<=n;++i){
      a[i].l=rd(),a[i].r=rd();a[i].id=i;
      if(a[i].r<a[i].l)a[i].r+=len;
       b[++top]=a[i].l;b[++top]=a[i].r;b[++top]=a[i].l-1;
    }
    sort(a+1,a+n+1);sort(b+1,b+top+1);
    top=unique(b+1,b+top+1)-b-1;
    for(int i=1;i<=n;++i){
        int x=lower_bound(b+1,b+top+1,a[i].l)-b;
        if(a[i].r>a[tag[x-1]].r)tag[x-1]=i;
    }
    for(int i=1;i<=top;++i)if(a[tag[i-1]].r>a[tag[i]].r)tag[i]=tag[i-1];
    for(int i=1;i<=n;++i){
        int x=lower_bound(b+1,b+top+1,a[i].r)-b;
        p[0][i]=tag[x];//cout<<a[i].r<<" "<<a[tag[x]].r<<endl;
    }
    for(int i=1;i<=21;++i)
      for(int j=1;j<=n;++j)p[i][j]=p[i-1][p[i-1][j]];
      ll an=2e9;
    for(int i=1;i<=n;++i){
        if(a[i].r-a[i].l+1==len)an=1;
        ll ans=2e9,t=a[i].l+len,num=0;
        int now=i;
        for(int j=21;j>=0;--j){
            int x=p[j][now];//if(j==21)cout<<a[x].r<<" "
            if(!x)continue;
            if(a[x].r>=t-1)ans=min(ans,num+(1<<j));
            else now=x,num+=(1<<j);
        }
        an=min(an,ans+1);
    }
   if(an==2e9)puts("impossible");else cout<<an;
    return 0;
}