[bzoj4444] 国旗计划 双指针+倍增

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

Sol

先破环为链(倍长一遍),然后我们发现我们可以维护一个东西叫做\(nex[i]\),表示i这个人最远能够接力到哪个人手上,我们把右端点排序之后发现满足单调性,直接用双指针扫一遍就可以了。

然后我们发现直接回答是\(O(n)\)的,但是这个显然可以倍增做,\(nex[i][j]\)表示j跳\(2^i\)步之后到的人,然后就能\(O(logn)\)回答。

注意如果最后一个完成任务的人不是当前查询的i(下一圈的时候),那么ans要++。具体画个图就明白了。

#include <bits/stdc++.h>
using namespace std;
struct nod{int l,r,id;friend bool operator<(nod a,nod b){if(a.r!=b.r) return a.r<b.r;return a.l<b.l;}}a[500005];
int n,m,tot,x,y,anc[20][500005],ans[500005];
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&x,&y);
        if(x>y) a[++tot]=(nod){x,y+m,i},a[++tot]=(nod){x+m,y+m+m,i};
        else a[++tot]=(nod){x,y,i},a[++tot]=(nod){x+m,y+m,i};
    }
    sort(a+1,a+tot+1);
    for(int i=1,j=2;i<=tot;i++){for(;j<=tot&&a[j].l<=a[i].r;j++);j--;anc[0][i]=(i==j)?0:j;}
    for(int j=1;j<=19;j++) for(int i=1;i<=tot;i++) anc[j][i]=anc[j-1][anc[j-1][i]];
    for(int i=1;i<=tot;i++)
    {
        if(a[i].l>m) continue;x=anc[0][i],y=1;
        for(int j=19;~j;j--) if(anc[j][x]&&a[anc[j][x]].r<a[i].l+m) x=anc[j][x],y+=(1<<j);
        x=anc[0][x],y++;
        ans[a[i].id]=y+(a[x].id!=a[i].id);
    }
    for(int i=1;i<=n;i++) printf("%d ",ans[i]);
}
posted @ 2018-08-22 21:46  CK6100LGEV2  阅读(275)  评论(0编辑  收藏  举报