P6563-[SBCOI2020]一直在你身旁【dp,单调队列】
正題
題目鏈接:https://www.luogu.com.cn/problem/P6563
題目大意
長度為nnn的序列aia_iai?,現(xiàn)在有一個隨機[1,n][1,n][1,n]的整數(shù),每次你可以花費aia_iai?詢問這個數(shù)字是否大于iii,求猜出所有數(shù)至少要多少花費。
T≤500,∑n≤7000T\leq 500,\sum n\leq 7000T≤500,∑n≤7000
保證aia_iai?單調(diào)不降
解題思路
考慮區(qū)間dpdpdp,設(shè)fl,rf_{l,r}fl,r?表示猜出區(qū)間[l,r][l,r][l,r]的最小花費。
最基本的轉(zhuǎn)移就是
 fl,r=min{max{fl,k,fk+1,r}+ak}(k∈[l,r))f_{l,r}=min\{\ max\{f_{l,k},f_{k+1,r}\}+a_k\ \}(\ k\in[l,r)\ )fl,r?=min{?max{fl,k?,fk+1,r?}+ak??}(?k∈[l,r)?)
然后考慮如何優(yōu)化轉(zhuǎn)移。
因為里面有個maxmaxmax,我們可以對于一個l,rl,rl,r考慮找到一個最小的zzz滿足fl,z>fz+1,rf_{l,z}>f_{z+1,r}fl,z?>fz+1,r?那么zzz以后的都是用fl,zf_{l,z}fl,z?,以前的都是用fz+1,rf_{z+1,r}fz+1,r?。
這個在右端點固定左端點向左時zzz是不升的,所以不用二分帶logloglog。
對于取fl,k+akf_{l,k}+a_kfl,k?+ak?的那一部分,aka_kak?和fl,zf_{l,z}fl,z?都隨著kkk增大不降,所以直接取fl,z+azf_{l,z}+a_zfl,z?+az?。
對于fk+1,r+akf_{k+1,r}+a_kfk+1,r?+ak?的那一部分,kkk的限制會不斷縮小,所以用一個單調(diào)隊列維護就可以了。
時間復(fù)雜度O(∑n2)O(\sum n^2)O(∑n2)
code
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; const int N=7200; int T,n,a[N]; long long f[N][N]; deque<int> q; long long calc(int k,int r){if(k<1)return 1e18;return f[r][k+1]+a[k]; } signed main() {scanf("%d",&T);while(T--){scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%d",&a[i]);for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)if(i!=j)f[i][j]=1e18; for(int r=2;r<=n;r++){q.clear();q.push_back(r-1);for(int l=r-1,z=r-1;l>=1;l--){while(z>l&&f[z-1][l]>f[r][z])z--;while(!q.empty()&&q.front()>=z)q.pop_front();if(!q.empty())f[r][l]=calc(q.front(),r);f[r][l]=min(f[r][l],f[z][l]+a[z]);if(l==1)continue;while(!q.empty()&&calc(q.back(),r)>=calc(l-1,r))q.pop_back();q.push_back(l-1);}}printf("%lld\n",f[n][1]);} }總結(jié)
以上是生活随笔為你收集整理的P6563-[SBCOI2020]一直在你身旁【dp,单调队列】的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: 数据库电脑配置要求(数据库 电脑配置)
 - 下一篇: P5470-[NOI2019]序列【模拟