AGC012 - E: Camel and Oases
原題鏈接
題意簡述
沙漠中有n(n≤2×105)個排成一條直線的綠洲,一頭儲水量為V(V≤2×105)的駱駝。
駱駝有兩個操作:
- 走到距離在V以內的一個綠洲。
- 飛到任意一個綠洲,但V減少一半。V=0時不能飛。
問駱駝依次從每個綠洲出發,能否一次性遍歷所有綠洲。
分析
首先預處理出 V=V0 時哪些綠洲之間是可以隨便走的,對于每個V0掃一遍即可。時間復雜度為O(nlog2V)。
每飛一次相當于下一層。題目轉化成欽定第一條線段,然后從每一層選一條線段,問能否覆蓋整個區間。
萬萬沒想到,這道題居然是狀壓DP!!!
s中的后起第i位為1表示從第i層選出了一條線段。
f1[s]表示狀態s時從1起向右最遠能延伸到哪,f2[s]表示狀態s時從n起向左最遠能延伸到哪。
f1[s]=max(f1[s],upFind(f1[s0]))
f2[s]=min(f2[s],lowFind(f2[s0]?1))
意義是從s0加上一條線段能延伸到哪。這個轉移方程和我的寫法有關,具體看代碼。
時間復雜度O(log2V?log2n?2log2V+1)=O(Vlog2nlog2V)
檢查答案時,對于第一層的每一條線段,尋找是否存在s,使得f1[s],f2[U?s?1]和該線段覆蓋整個區間。表示用狀態s這些線段盡可能擴展左半部分,用剩下的線段(不包括第一層)盡可能擴展右半部分,再加上第一層的這條線段。
最大時間復雜度O(n?2log2V)=O(nV),GG ╮(╯﹏╰)╭
但實際上,第一層的線段條數是不能超過log2V+1的。因為飛log2V+1次后V0=0,并且下一層的條數比上一層只多不少,要是第一層就超過log2V+1那么不可能遍歷所有綠洲。
所以最大時間復雜度為O(Vlog2V)。
總時間復雜度最大為O(nlog2V+Vlog2nlog2V)。
實現
a[i][j]記錄第i層的第j條線段的右端點。特別地,a[i][0]記錄第i層線段的條數。
upFind(x)找出第一個嚴格大于x的右端點。這個右端點所在的區間一定能延伸當前的f1。
lowFind(x?1)找出第一個嚴格小于x-1的右端點。這個右端點的下一個區間一定能延伸當前的f2。如果寫lowFind(x),要是找到x-1的話,說明有一段以x-1為右端點的區間以及一段以x為左端點的區間。這時候明明可以加入前者,實際上卻加入了后者,導致問題。
代碼
//Camel and Oases #include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long lint; int const N=2e5+10; int const S=1<<19; int n,V; lint d[N]; int logV,a[25][N]; int U,f1[S],f2[S]; int upFind(int a[],int x) {int L=1,R=a[0];while(L<R-1){int mid=(L+R)>>1;if(a[mid]<=x) L=mid+1;if(a[mid]>x) R=mid;}if(a[L]>x) return a[L];else return a[R]; } int lowFind(int a[],int x) {int L=1,R=a[0];while(L<R-1){int mid=(L+R)>>1;if(a[mid]<x) L=mid;if(a[mid]>=x) R=mid-1;}if(a[R]<x) return a[R]+1;else return a[L]+1; } int main() {scanf("%d%d",&n,&V);logV=0;while((1<<logV)<=V) logV++;logV++;for(int i=1;i<=n;i++) scanf("%lld",&d[i]),d[i-1]=d[i]-d[i-1];d[n]=0;for(int i=1;i<=logV;i++){a[i][0]=1;for(int j=1;j<=n;j++){a[i][a[i][0]]=j;if(d[j]>(V>>(i-1))) a[i][0]++;}}if(a[1][0]>logV){for(int i=1;i<=n;i++) printf("Impossible\n");return 0;}U=(1<<logV)-1;for(int s=0;s<=U;s++) f1[s]=0,f2[s]=n+1;for(int s=0;s<=U;s+=2)for(int i=2;i<=logV;i++){int s0=1<<(i-1);if(s&s0) continue;f1[s|s0]=max(f1[s|s0],upFind(a[i],f1[s]));f2[s|s0]=min(f2[s|s0],lowFind(a[i],f2[s]-1));}for(int i=1;i<=a[1][0];i++){bool f=false;int fr=a[1][i-1]+1,to=a[1][i];if(i==1) fr=1;for(int s=0;s<=U&&!f;s+=2)if(fr<=f1[s]+1 && f2[U-s-1]-1<=to) f=true;if(f) for(int j=fr;j<=to;j++) printf("Possible\n");else for(int j=fr;j<=to;j++) printf("Impossible\n");}return 0; }注意
- 因為最多飛log2V+1次,所以全集狀態U=2log2V+1?1,體現在題目中為219,而不是218。
- a[i][0]做他用,有些地方寫的可能會麻煩一些。
轉載于:https://www.cnblogs.com/VisJiao/p/8485770.html
總結
以上是生活随笔為你收集整理的AGC012 - E: Camel and Oases的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CVE-2020-10148: Sola
- 下一篇: translator什么意思中文_tra