[bzoj4444][乱搞]国旗计划

55 篇文章 0 订阅

Description

A国正在开展一项伟大的计划——国旗计划。这项计划的内容是边防战士手举国旗环绕边境线奔袭一圈。这
项计划需要多名边防战士以接力的形式共同完成,为此,国土安全局已经挑选了N名优秀的边防战上作为这 项计划的候选人。
A国幅员辽阔,边境线上设有M个边防站,顺时针编号1至M。每名边防战士常驻两个边防站,并且善于
在这两个边防站之间长途奔袭,我们称这两个边防站之间的路程是这个边防战士的奔袭区间。n名边防战士
都是精心挑选的,身体素质极佳,所以每名边防战士的奔袭区间都不会被其他边防战士的奔袭区间所包含。
现在,国十安全局局长希望知道,至少需要多少名边防战士,才能使得他们的奔袭区间覆盖全部的边境线,
从而顺利地完成国旗计划。不仅如此,安全局局长还希望知道更详细的信息:对于每一名边防战士,在他必
须参加国旗计划的前提下,至少需要多少名边防战士才能覆盖全部边境线,从而顺利地完成国旗计划。

Input

第1行,包含2个正整数N,M,分别表示边防战士数量和边防站数量。
随后n行,每行包含2个正整数。其中第i行包含的两个正整数Ci、Di分别表示i号边防战士常驻的两个边防站编号,
Ci号边防站沿顺时针方向至Di号边防站力他的奔袭区间。数据保证整个边境线都是可被覆盖的。

Output

输出数据仅1行,需要包含n个正整数。其中,第j个正整数表示j号边防战士必须参加的前提下至少需要 多少名边防战士才能顺利地完成国旗计划

Sample Input

4 8

2 5

4 7

6 1

7 3

Sample Output

3 3 4 3

HINT

n≤2×10^5,M< 10^9,1≤Ci,Di≤M

题解

先预处理出每个人右边应该放哪个人,肯定放l和他的r相差最小的嘛,因为不可能有包含的情况出现
之后可以预处理出一个pos[i][j]表示第i个人向右放2^j个人的最后一个人的编号
然后随便做。。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
struct node{LL l,r;int op,next;}a[410000];
bool cmpx(node n1,node n2){return n1.l<n2.l;}
int n;LL m;
int answer[410000];
int getnext(int u)
{
    int l=1,r=2*n,ret;
    while(l<=r)
    {
        int mid=(l+r)/2;
        if(a[mid].l<=a[u].r)ret=mid,l=mid+1;
        else r=mid-1;
    }
    return ret;
}
int pos[410000][25],bin[25];//i~i+2^j的最后一个人 
int getans(int u)
{
    LL x=a[u].l;int ret=1;
    for(int i=20;i>=0;i--)
        if(pos[u][i]!=-1 && a[pos[u][i]].r-x<m)u=pos[u][i],ret+=bin[i];
    if(a[u].r-x<m)ret++;
    return ret;
}
int main()
{
    //freopen("flag7.in","r",stdin);
    //freopen("a.out","w",stdout);
    bin[0]=1;for(int i=1;i<=20;i++)bin[i]=bin[i-1]*2;
    scanf("%d%lld",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld%lld",&a[i].l,&a[i].r);a[i].op=i;
        if(a[i].r<a[i].l)a[i].r+=m;
    }
    for(int i=1;i<=n;i++)
        a[i+n].l=a[i].l+m,a[i+n].r=a[i].r+m,a[i+n].op=0;
    sort(a+1,a+1+2*n,cmpx);
    memset(pos,-1,sizeof(pos));
    for(int i=1;i<=2*n;i++)a[i].next=getnext(i),pos[i][0]=a[i].next;
    for(int i=1;bin[i]<=2*n-1;i++)
        for(int j=1;j<=2*n;j++)
        {
            if(2*n-j>=bin[i]&& pos[j][i-1]!=-1&& pos[pos[j][i-1]][i-1]!=-1)pos[j][i]=pos[pos[j][i-1]][i-1];
        }
    for(int i=1;i<=2*n;i++)
        if(a[i].op)answer[a[i].op]=getans(i);
    for(int i=1;i<=n;i++)printf("%d ",answer[i]);
    //printf("\n");
    //printf("%d\n",cnt);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值