【BZOJ4444】[Scoi2015]国旗计划
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
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
题解:遇到环的题,显然要将环倍长一遍变成链做。
先排序,然后对于每个战士,他一定是传给他能传到的,右端点最远的战士,所以可以用双指针法搞定。
然后用倍增的思想,用to[i][j]表示i往后传2^j次会传给谁,查询时像倍增求lca一样查询就行了。
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn=200010; struct node { int a,b,org; node(){} node(int x,int y,int z){a=x,b=y,org=z;} }p[maxn<<1]; int n,m,tot; int to[20][maxn<<1],ans[maxn]; inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<'0'||gc>'9') {if(gc=='-')f=-f; gc=getchar();} while(gc>='0'&&gc<='9') ret=ret*10+gc-'0',gc=getchar(); return ret*f; } bool cmp(const node &a,const node &b) { return a.b<b.b; } int main() { n=rd(),m=rd(); int i,j,a,b; for(i=1;i<=n;i++) { a=rd(),b=rd(); if(a>b) p[++tot]=node(a,b+m,i),p[++tot]=node(a+m,b+m+m,i); //这里把后半句去掉则会WA,不明觉厉 else p[++tot]=node(a,b,i),p[++tot]=node(a+m,b+m,i); } sort(p+1,p+tot+1,cmp); for(i=j=1;i<=tot;i++) { for(;j<tot&&p[j+1].a<=p[i].b;j++); to[0][i]=(i==j)?0:j; } for(j=1;(1<<j)<=tot;j++) for(i=1;i<=tot;i++) to[j][i]=to[j-1][to[j-1][i]]; for(i=1;i<=tot;i++) { if(p[i].a>m) continue; a=i,b=0; for(j=19;j>=0;j--) if(to[j][a]&&p[to[j][a]].b<p[i].a+m) a=to[j][a],b+=(1<<j); a=to[0][a],b++; ans[p[i].org]=b+(p[i].org!=p[a].org); } for(i=1;i<=n;i++) { printf("%d",ans[i]); if(i<n) printf(" "); } return 0; }