【bzoj4444】国旗计划
Description
A国正在开展一项伟大的计划——国旗计划。这项计划的内容是边防战士手举国旗环绕边境线奔袭一圈。这项计划需要多名边防战士以接力的形式共同完成,为此,国土安全局已经挑选了N名优秀的边防战上作为这项计划的候选人。
A国幅员辽阔,边境线上设有M个边防站,顺时针编号1至M。每名边防战士常驻两个边防站,并且善于在这两个边防站之间长途奔袭,我们称这两个边防站之间的路程是这个边防战士的奔袭区间。n名边防战士都是精心挑选的,身体素质极佳,所以每名边防战士的奔袭区间都不会被其他边防战士的奔袭区间所包含。
现在,国家安全局局长希望知道,至少需要多少名边防战士,才能使得他们的奔袭区间覆盖全部的边境线,从而顺利地完成国旗计划。不仅如此,安全局局长还希望知道更详细的信息:对于每一名边防战士,在他必须参加国旗计划的前提下,至少需要多少名边防战士才能覆盖全部边境线,从而顺利地完成国旗计划。
Solution
由于要选取最少的区间来覆盖整个圈,那么如果选择了某个区间,下一个区间的选择一定是所有左端点小于等于该区间右端点的区间中,右端点最靠后的那个。
边境线为环不好处理,我们可以将其断开成链后倍长处理。我们可以建立ST表,用f[i][j]表示从第i个区间开始挑选2j个边防战士所能覆盖区间的最右端。
预处理出f数组后,倍增求解。对于每一个i,从大到小枚举j,若加入一段不能覆盖整个圈则加入,否则不加入,最后答案+2即可(必选的边防战士和最后一段)。
区间跨越m需特别处理,时间复杂度O(nlogn)。
Code
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 struct node{ 6 int l,r,id; 7 }a[400010]; 8 int n,m,ans[200010],f[400010][20]={0}; 9 bool vis[200010]={false}; 10 bool cmp(node a,node b){ 11 return (a.l!=b.l)?a.l<b.l:a.r<b.r; 12 } 13 int solve(int x){ 14 int res=0,s=x,ed=a[x].l+m; 15 while(a[f[s][0]].r<ed){ 16 for(int j=19;~j;j--) 17 if(f[s][j]&&a[f[s][j]].r<ed) 18 s=f[s][j],res+=(1<<j); 19 } 20 return res; 21 } 22 int main(){ 23 scanf("%d%d",&n,&m); 24 for(int i=1;i<=n;i++){ 25 int l,r; 26 scanf("%d%d",&l,&r); 27 if(l<=r){ 28 a[i]=(node){l,r,i}; 29 a[n+i]=(node){l+m,r+m,i}; 30 } 31 else{ 32 a[i]=(node){l,r+m,i}; 33 a[n+i]=(node){l+m,r+m+m,i}; 34 } 35 } 36 n<<=1; 37 sort(a+1,a+n+1,cmp); 38 for(int i=n-1,j=n;i;i--){ 39 while(a[j].l>a[i].r) 40 j--; 41 f[i][0]=j; 42 } 43 for(int i=n-1;i;i--) 44 for(int j=1;j<20;j++) 45 f[i][j]=f[f[i][j-1]][j-1]; 46 for(int i=1;i<=n;i++) 47 if(!vis[a[i].id]){ 48 vis[a[i].id]=true; 49 ans[a[i].id]=solve(i); 50 } 51 for(int i=1;i<=(n>>1);i++) 52 printf("%d ",ans[i]+2); 53 puts(""); 54 return 0; 55 }