标签:如何 pos while display 需要 sum 维护 总结 lan
有一个长度为\(n\)的数组\(a_1,a_2,\dots ,a_n\),\(a_i\in\{-1,0,1\}\)。请你将这个数组,划分为连续的\(m\geq 1\)段(每个位置都恰好位于某一段内),使得每一段的长度,都大于等于\(L\),小于等于\(R\)。
我们定义,一段连续子序列\(a_l,a_{l+1},\dots ,a_r\)的价值为:
也可以用公式表示为:\(v(l,r)=[(\sum_{i=l}^{r}a_i)>0]-[(\sum_{i=l}^{r}a_i)<0]\)。
请你通过划分,最大化所有\(m\)段的价值之和(\(m\)是你自己选的,并不是题目规定的)。
数据范围:\(1\leq L\leq R\leq n\leq 10^6\)。\(T\)组测试数据,\(1\leq T\leq 1000\),\(\sum n\leq 9\cdot 10^6\)。
考虑DP。设\(dp[i]\)表示把\(a_1\dots a_i\)划分为若干段,并且\(a_i\)是最后一段的结尾时,的最大价值。
朴素的转移很简单:
其中\(v\)表示一段连续子序列的价值,题面里已给出定义。按这个式子直接DP,时间复杂度\(O(n^3)\)。
我们可以对\(a\)序列做前缀和。设\(s_i=\sum_{j=1}^{i}a_i\)。于是转移式改写为:
时间复杂度\(O(n^2)\)。
继续优化。首先,因为每个\(i\)只能从一段特定的区间转移过来,容易想到单调队列优化DP:队列从前到后,“价值”单调递减,位置单调递增,也就是永远不保留“又老又不中用”的元素。
然而会遇到一个问题:你如何比较两个元素的“价值”大小呢?我们不能只比较\(dp[j]\)的大小,因为还有后面的\(s_j\)的贡献。但又不能简单地把\(dp[j]-s_j\)作为价值,因为\(s_j\)是要和后面的每个\(i\)共同算出一个价值\(v\in\{-1,0,1\}\)。
既然把它们放在一起,怎么比较都不公平,那不如将它们分开来:对每种\(s_j\)的值(从最小的\(-n\)到最大的\(n\),共有\(2n+1\)种可能的值),各开一个单调队列。此时同一个单调队列里,就只需要比\(dp[j]\)的大小了。
对于每个值\(x\) (\(-n\leq x\leq n\)),我们维护出它的队首(最优的),记其DP值为\(f[x]\)。特别地,如果单调队列为空,则\(f[x]=\inf\)。
每次转移前,先将\(i-L\)入队,将\(i-R-1\)出队。只需要在对应的\(x=s_{i-L}\)和\(x=s_{i-R-1}\)这两个队列里做修改,修改后更新对应的\(f[x]\)的值。这都是单调队列的基本操作。
单调队列是维护好了,怎么转移呢?我们总不能枚举所有\(x\)吧?考虑,使得\(v(j+1,i)=1\)的\(j\),一定是\(s_i-s_j>0\),也就是\(s_j<s_i\),所以这样的\(s_j\)值是一段前缀(即\(x\in[-n,s_i-1]\));同理,使得\(v(j+1,i)=-1\)的\(j\),一定是一段后缀(\(x\in[s_i+1,n]\));而使得\(v(j+1,i)=0\)的\(s_j\),就是\(x=s_i\)。所以,我们只需要在\(f\)数组上做3次区间最大值查询,就能完成转移了!
总结一下,我们在\(\text{push},\text{pop}\)一个元素,也就是对单调队列做改动时,要支持对\(f\)数组单点修改。因为每个\(j\)只会入队、出队一次,所以总修改次数是\(O(n)\)的。在转移时,需要做3次区间最大值查询,总查询次数也是\(O(n)\)的。用线段树来维护这个\(f\)数组即可。时间复杂度\(O(n\log n)\)。
单调队列,可以用\(\texttt{vector}\)维护(\(\texttt{deque}\)时间空间常数都太大了)。\(\texttt{pop_front}\)操作,可以用变量记录每个\(\texttt{vector}\)的实际队首元素在哪里,要\(\texttt{pop_front}\)时令这个队首位置\(\texttt{++}\)即可。因为每个元素只会入队一次,所以空间复杂度是\(O(n)\)的。
总时间复杂度\(O(n\log n)\),空间复杂度\(O(n)\)。
参考代码(本代码仅供参考,实际提交时建议使用读入优化,详见本博客公告栏):
//problem:1002
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
template<typename T>inline void ckmax(T& x,T y){x=(y>x?y:x);}
template<typename T>inline void ckmin(T& x,T y){x=(y<x?y:x);}
const int MAXN=1e6,INF=1e9;
int n,L,R,a[MAXN+5],s[MAXN+5],dp[MAXN+5];
int lpt[MAXN*2+5],bas;
vector<int>vec[MAXN*2+5];
struct SegmentTree{
int mx[MAXN*8+100];
void build(int p,int l,int r){
mx[p]=-INF;
if(l==r) return;
int mid=(l+r)>>1;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
}
void modify(int p,int l,int r,int pos,int v){
if(l==r){
mx[p]=v;
return;
}
int mid=(l+r)>>1;
if(pos<=mid)
modify(p<<1,l,mid,pos,v);
else
modify(p<<1|1,mid+1,r,pos,v);
mx[p]=max(mx[p<<1],mx[p<<1|1]);
}
int query(int p,int l,int r,int ql,int qr){
if(ql<=l && qr>=r)
return mx[p];
int mid=(l+r)>>1;
int res=-INF;
if(ql<=mid)
res=query(p<<1,l,mid,ql,qr);
if(qr>mid)
ckmax(res,query(p<<1|1,mid+1,r,ql,qr));
return res;
}
SegmentTree(){}
}T;
void ins(int p){
int vv=s[p]+bas;
while(SZ(vec[vv])>lpt[vv] && dp[vec[vv].back()]<=dp[p])
vec[vv].pop_back();
vec[vv].pb(p);
T.modify(1,1,n+bas,vv,(SZ(vec[vv])>lpt[vv] ? dp[vec[vv][lpt[vv]]] : -INF));
}
void del(int p){
int vv=s[p]+bas;
if(SZ(vec[vv])>lpt[vv] && vec[vv][lpt[vv]]==p)
++lpt[vv];
T.modify(1,1,n+bas,vv,(SZ(vec[vv])>lpt[vv] ? dp[vec[vv][lpt[vv]]] : -INF));
}
void solve_case(){
cin>>n>>L>>R;
for(int i=1;i<=n;++i){
cin>>a[i];
s[i]=s[i-1]+a[i];
}
bas=n+1;
for(int i=-n;i<=n;++i){
vector<int>().swap(vec[i+bas]);
lpt[i+bas]=0;
}
T.build(1,1,n+bas);
for(int i=L;i<=n;++i){
int mid=s[i]+bas;
if(i-R-1==0 || i-R-1>=L){
del(i-R-1);
}
if(i-L==0 || i-L>=L){
ins(i-L);
}
dp[i]=-INF;
if(mid>1){
ckmax(dp[i],T.query(1,1,n+bas,1,mid-1)+1);
}
ckmax(dp[i],T.query(1,1,n+bas,mid,mid));
if(mid<n+bas){
ckmax(dp[i],T.query(1,1,n+bas,mid+1,n+bas)-1);
}
}
cout<<dp[n]<<endl;
}
int main() {
int T;cin>>T;while(T--){
solve_case();
}
return 0;
}
标签:如何 pos while display 需要 sum 维护 总结 lan
原文地址:https://www.cnblogs.com/dysyn1314/p/13501291.html