[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;
}

转载于:https://www.cnblogs.com/ZH-comld/p/10260417.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值