1. 程式人生 > >【AGC012E】 Camel and Oases ST表+狀壓dp

【AGC012E】 Camel and Oases ST表+狀壓dp

題目大意:一排點,兩點間有距離。 初始你有一個行走值$v$,如果相鄰兩點距離不超過$v$你可以自由在這兩點行走。 
當$v$大於$0$時,你可以選擇某一時刻突然飛到任意點,這樣做後$v$會減半(下取整)。 問從每個位置初始出發能否到達所有位置。

點的數量$≤2*10^5$,$v≤2*10^5$,$|兩點距離|≤10^9$。

 

我們令$l[i][j]$表示從$i$出發,一路往左走,經過所有長度不超過$v>>j$(此處的$>>$表示右移,以下都是)的邊,能走到最左的點的編號。

令$r[i][j]$表示從$i$出發,一路往右走,經過所有長度不超過$v>>j$的邊,能走到最右的點的編號。

令$n$表示點的數量,$m=\lceil log_2v\rceil$。

我們不難得出:從$u$號點出發,是否可以遍歷完所有點的判斷條件,可以轉化為:

是否可以將點集分成$m+1$個塊,且第$i$(從$0$到$m$)個塊內邊的長度均不超過$v>>i$,且第$u$號點需要在第$0$個塊內。

 

那麼,對於$[1,2^m)$中的每一個$i$($i$是一個二進位制狀態,$i$的第$j$($j$從$1$到$m$)位為$1$表示選擇了圖中第$j$個塊)

求一個最大的$f[i]$,滿足區間$[1,f[i]]$中的點能分成由狀態i表示的若干個塊。

同理,求一個最小的$g[i]$,滿足區間$[g[i],n]$中的點能分成由狀態i表示的若干個塊。

求這個可以通過l和r的值+狀壓$dp$實現,時間複雜度是$O(2^{m})$。

我們令$o=2^m-2$。

我們發現,若存在$i$,使得$r[f[i]][0]>=l[g[o$^$i]][0]$,那麼從區間$[\ l[g[o$^$i]][0]\ ,\ r[f[i]][0]\ ]$中出發的點,顯然可以遍歷玩所有點。

我們可以$O(1)$打上一個標記,求答案的時候$O(n)$掃一遍,判斷某個點是否被打了標記即可。

總時間複雜度:$O(n\ log\ v+2^{\lceil log_2v \rceil})$。

 1 #include<bits/stdc++.h>
 2 #define
M 400005 3 #define YXQAK printf("Possible\n") 4 #define XFZBL printf("Impossible\n"); 5 using namespace std; 6 7 int a[M]={0},n,m,v,l[20][M]={0},r[20][M]={0}; 8 int f[M]={0},g[M]={0},p[M]={0}; 9 10 int main(){ 11 scanf("%d%d",&n,&v); 12 for(int i=1;i<=n;i++) scanf("%d",a+i); 13 sort(a+1,a+n+1); 14 for(int j=0,V=v;V;j++,V>>=1){ 15 m=max(m,j); 16 for(int i=1;i<=n;i++){ 17 int I=i+1; 18 while(I<=n&&a[I]-a[I-1]<=V) I++; 19 I--; 20 for(int ii=i;ii<=I;ii++) 21 l[j][ii]=i,r[j][ii]=I; 22 i=I; 23 } 24 } 25 m++; 26 for(int i=1;i<=n;i++) l[m][i]=r[m][i]=i; 27 for(int i=0;i<(1<<m);i++) g[i]=n; f[0]=1; 28 for(int i=1;i<(1<<m);i++){ 29 int now=1; 30 for(int j=m-1;~j;j--) 31 if((1<<j)&i) 32 f[i]=max(f[i],r[j+1][f[i^(1<<j)]]+1); 33 34 now=n; 35 for(int j=m-1;~j;j--) 36 if((1<<j)&i) 37 g[i]=min(g[i],l[j+1][g[i^(1<<j)]]-1); 38 } 39 40 for(int i=0;i<(1<<m);i++){ 41 if(r[0][f[i]]+1>=l[0][g[(1<<m)-i-1]]) 42 p[l[0][g[(1<<m)-i-1]]]++,p[r[0][f[i]]+1]--; 43 } 44 for(int i=1;i<=n;i++){ 45 p[i]+=p[i-1]; 46 if(p[i]) YXQAK; 47 else XFZBL; 48 } 49 }