1. 程式人生 > >[BZOJ4444][SCOI2015]國旗計劃(倍增)

[BZOJ4444][SCOI2015]國旗計劃(倍增)

鏈上是經典貪心問題,將線段全按左端點排序後把點全撒線上段右端點上。這裡放到環上,倍長即可。

題目保證不存在區間包含情況,於是有一種暴力做法,先將戰士的管轄區間按左端點從小到大排序,對於詢問x,從x戰士出發,每次走到最遠(管轄區間左端點最大)的一個戰士,滿足這個戰士的區間左端點被x的區間右端點覆蓋到,然後讓這個戰士接替x繼續傳遞。$O(n^2)$

這顯然可以倍增優化,f[i][j]表示從戰士i開始共$2^j$個戰士參與傳遞,最遠可以傳遞到哪個戰士的手中。$O(n\log n)$

 1 #include<cstdio>
 2 #include<algorithm>
 3
#define rep(i,l,r) for (int i=(l); i<=(r); i++) 4 using namespace std; 5 6 const int N=400010; 7 int n,m,ans[N],f[N][20]; 8 9 struct P{ int l,r,id; }a[N]; 10 bool operator <(const P &a,const P &b){ return a.l<b.l; } 11 12 int main(){ 13 freopen("bzoj4444.in","r",stdin);
14 freopen("bzoj4444.out","w",stdout); 15 scanf("%d%d",&n,&m); 16 rep(i,1,n){ 17 scanf("%d%d",&a[i].l,&a[i].r); a[i].id=i; 18 if (a[i].l>a[i].r) a[i].r+=m; 19 } 20 sort(a+1,a+n+1); 21 rep(i,n+1,n*2) a[i]=(P){a[i-n].l+m,min(m*2,a[i-n].r+m),a[n-i].id};
22 int R=1; 23 rep(i,1,n*2){ 24 while (R<n*2 && a[R+1].l<=a[i].r) R++; 25 f[i][0]=R; 26 } 27 rep(j,1,19) rep(i,1,n*2) f[i][j]=f[f[i][j-1]][j-1]; 28 rep(i,1,n){ 29 int x=i,cnt=1; 30 for (int j=19; ~j; j--) if (f[x][j]-i<n) cnt+=1<<j,x=f[x][j]; 31 ans[a[i].id]=cnt; 32 } 33 rep(i,1,n) printf("%d ",ans[i]); 34 return 0; 35 }