AT2365-[AGC012E]Camel and Oases【状压dp】
正題
題目鏈接:https://www.luogu.com.cn/problem/AT2365
題目大意
一個數(shù)軸上有nnn個點,開始你有個水壺容量為VVV,你每次有兩個操作
- 走到一個距離與你不超過VVV的點
- 讓V=?V2?V=\lfloor\frac V2\rfloorV=?2V??,然后跳到任意一個點。
對于每個點求從這個點出發(fā)能否走到所有點。
1≤n,V≤2×105,?109≤xi≤1091\leq n,V\leq 2\times 10^5,-10^9\leq x_i\leq 10^91≤n,V≤2×105,?109≤xi?≤109
解題思路
顯然操作二的次數(shù)不會超過log?V\log VlogV,考慮從這里入手。考慮有沒有一種方案能夠把序列分成若干段,每一段相鄰的差不超過給其匹配的一個?V2x?\lfloor\frac{V}{2^x}\rfloor?2xV??且VVV的段剛好在起點。
并且因為nnn很大我們可以考慮改成用數(shù)字而不是下標(biāo)維護它這一維,設(shè)fSf_{S}fS?表示用集合SSS中的VVV能夠從右邊走到的最左邊的位置,同理gSg_SgS?表示從左邊開始走,然后枚舉總間段。
注意到這樣的復(fù)雜度是O(nV)O(nV)O(nV)的,不過如果一段的左右都已經(jīng)分出了log?V\log VlogV個段,那么這個位置也是不合法的(因為)。
時間復(fù)雜度:O(Vlog?V)O(V\log V)O(VlogV)
code
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=2e5+10,M=19; int n,m,V,w[M],x[N],l[M][N],r[M][N],f[1<<M],g[1<<M],ans[N]; int main() {scanf("%d%d",&n,&V);w[0]=V;while(V){V>>=1;w[++m]=V;}swap(w[0],w[m]);for(int i=1;i<=n;i++)scanf("%d",&x[i]);for(int j=0;j<=m;j++){l[j][1]=1;r[j][n]=n;for(int i=2;i<=n;i++)l[j][i]=(x[i]-x[i-1]<=w[j])?l[j][i-1]:i;for(int i=n-1;i>=1;i--)r[j][i]=(x[i+1]-x[i]<=w[j])?r[j][i+1]:i;}int MS=(1<<m);for(int s=0;s<MS;s++){f[s]=n+1;for(int i=0;i<m;i++){if(!((s>>i)&1))continue;f[s]=min(f[s],l[i][f[s^(1<<i)]-1]);g[s]=max(g[s],r[i][g[s^(1<<i)]+1]);}}int cnt=0;for(int i=1;i<=n;i++){bool flag=0;for(int s=0;s<MS;s++){int t=(MS-1)^s;if(g[s]>=i-1&&f[t]<=r[m][i]+1){flag=1;break;}}for(int j=i;j<=r[m][i];j++)ans[j]|=flag;cnt++;i=r[m][i];if(cnt>m)break;}cnt=0;for(int i=n;i>=1;i--){bool flag=0;for(int s=0;s<MS;s++){int t=(MS-1)^s;if(f[s]<=i+1&&g[t]>=l[m][i]-1){flag=1;break;}}for(int j=i;j>=l[m][i];j--)ans[j]|=flag;cnt++;i=l[m][i];if(cnt>m)break;}for(int i=1;i<=n;i++)if(ans[i])puts("Possible");else puts("Impossible");return 0; }總結(jié)
以上是生活随笔為你收集整理的AT2365-[AGC012E]Camel and Oases【状压dp】的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 手机截屏快捷键大全
- 下一篇: 华为荣耀路由Pro隐藏WiFi怎么设置怎