单调栈和单调队列专题
简介
内容
通过时间复杂度为\(O(n)\)维护静态的区间最值,虽然是静态,但是时间比\(ST\)表和线段树更优秀。
实现
顾名思义,单调队列维护一个内部数据为单调的队列。采用合适的方式进行出队和入队操作,让每一个区间的最大、最小值就是队列的头或尾。与一般队列不一样的是,单调队列是一个双向队列,也就是可以在队列的头或尾进行出队入队操作。实现时可以使用数组模拟。
习题
T1 CF940E - Cashback
这个题就是在每次在更新的时候发现取用长度大于\(c\)时,就可以看成一个长度为\(c\)的区间加上一些单独的区间,一个长度大于\(2*c\)的区间可以通过变为两个单独的区间实现2更优,需要维护长度为\(c\)的最小值即可,采用\(dp\),但是我用了线段树。
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5;
int n,a[N],dp[N],minn[N<<2],sum[N],c;
void build(int id,int l,int r){if(l==r){minn[id]=a[l];return ;}int mid=(l+r)>>1;build(id<<1,l,mid);build(id<<1|1,mid+1,r);minn[id]=min(minn[id<<1],minn[id<<1|1]);
}
int query(int id,int l,int r,int L,int R){if(L<=l&&r<=R)return minn[id];int mid=(l+r)>>1;int res=0x3f3f3f3f;if(L<=mid)res=min(res,query(id<<1,l,mid,L,R));if(R>mid)res=min(res,query(id<<1|1,mid+1,r,L,R));return res;
}
signed main(){//相必是一道dp的题目//现在这个块有几个是必不可少的//所以这题需要使用滚动数组? cin>>n>>c;for(int i=1;i<=n;i++)cin>>a[i],sum[i]=sum[i-1]+a[i],dp[i]=sum[i];build(1,1,n);for(int i=1;i<=n;i++){if(i>=c)dp[i]=min(dp[i],dp[i-c]+sum[i]-sum[i-c]-query(1,1,n,i-c+1,i));dp[i]=min(dp[i],dp[i-1]+a[i]);}cout<<dp[n];
}