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号边防战士必须参加的前提下至少需要多少名边防战士才能顺利地完成国旗计划.
HINT
$n\leq 2\times 10^5,M<10^9,1\leq C_i,D_i\leq M$
题解:
一个环的题容易想到倍长变成链,然后对于点$i$向右跑到了点$i+m$就算跑完了;
考虑算出每个点向右一次能传递到的最右端的节点,设为$next[i]$,将所有人按照左端点排序,找到左端点不超过当前节点的右端点最大的即可;由于排序后左节点单调上升,所以找到的人也肯定单调向右,所以可以直接用一个指针扫,线性复杂度计算;
然后一个显然的做法是倍增,用类似树上倍增的方法维护每个$i$到$next[i]$的这个类似树的结构,然后直接跑就行了。
但是由于本人过于傻逼,以为倍增完还要二分找答案,算了算40w两个$log$正好1.2S,然后不知道谁说这题一秒时限肯定会卡我(实际上这题后来改成了两秒并且倍增只有一个$log$),于是自爆的cha掉了这个算法。。。
进一步看这棵next树,设当前节点为$x$,$next[x]$能走到的最远节点为$k$,那么显然$next[x]$和$k$之间的距离至少为$m$,所以$x$到$k$的距离一定大于$m$,所以$x$能走到的最远节点$k'$一定在$next[x]$和$k$之间,如图:
这个性质就是说,每次做出的决策点肯定不会比上一次(父节点)的决策点更右,即深度是保持不降的,那么就可以用双端队列暴力维护进出队,统计答案就是$dep[x]-dep[k]+1$。
后面的部分只需要一个dfs,所以时间复杂度是线性的,但是前面要排序,所以是$O(nlogn)$。。。
代码:
1 #include2 #include 3 #include 4 #include 5 #include 6 using namespace std; 7 typedef long long ll; 8 struct edge{ 9 int v,next;10 }a[500001];11 struct task{12 ll l,r;13 int id;14 }q[500001];15 int n,m,p=1,tot=0,dep[500001],ans[500001],head[500001],hd,ta,qq[2000001];16 ll l,r;17 bool cmp(task a,task b){18 return a.l =q[u].l+m)now++;30 ans[q[u].id]=dep[u]-dep[qq[now]]+1;31 }32 for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){33 int v=a[tmp].v;34 dfs(v,now,dpt+1);35 }36 ta--;37 }38 int main(){39 memset(head,-1,sizeof(head));40 scanf("%d%d",&n,&m);41 for(int i=1;i<=n;i++){42 scanf("%d%d",&l,&r);43 if(l>r)r+=m;44 q[i].l=l;45 q[i].r=r;46 q[i].id=i;47 q[i+n].l=l+m;48 q[i+n].r=r+m;49 q[i+n].id=0;50 }51 sort(q+1,q+n+1,cmp);52 for(int i=1;i