U498421 切序列 题解
U498421 切序列 题解
设 \(f_i\) 表示考虑前 \(i\) 个数时的答案。令 \(s_i=\sum_{j=1}^ia_j\),枚举上一段的划分,有转移:
\[f_i=\min_{j<i\land |s_i-s_j|\le m}\{f_{j}+\max_{j<k\le i}a_k\}
\]
用 ST 实现区间最值有复杂度 \(\mathcal O(n^2)\)。
问题出在:我们要同时考虑 \(i,s_i\) 两维限制,于是可以想到用 CDQ 降维。$|s_i-s_j|\le m\implies s_j-m \le s_i\le s_j+m $
考虑 CDQ 时,已经处理了\([l,mid]\) 的DP值,现在要实现 \([l,mid]\to (mid,r]\) 的转移。
那么显然只需要将 \((mid,r]\) 中的元素按照 \(s\) 排序,对于一个 \(j\) 来说,可以贡献到的 \(i\) 变成了连续一段。
现在考虑 \(\max\) 的贡献。
令\( h_i=\left\{\begin{aligned} &\max_{i\le j\le mid}a_j&j\le mid\\ &\max_{mid<j\le i}a_j&j>mid \end{aligned}\right. \),则 \(f_{j}+\max\{h_i,h_j\}\to f_i\)
这时我们再分类讨论拆 \(\max\),即:
\[f_i=\max\{\max_{h_j\le h_i}\{f_j\}+h_i,\max_{h_j>h_i}\{f_j+h_j\}\}
\]
现在 \(j\to i\) 的转移受到 \(s,h\) 的限制,然而 \(s\) 经过前文的分析已经变成了区间,可以用扫描线解决,再用一个建在 \(h\) 上的线段树维护上述两种情况更新即可。
总复杂度 \(\mathcal O(n\log^2n)\)。
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int,int> pa;
const int NN=1e5+5,INF=0x3f3f3f3f3f3f3f3f;
int n,m,a[NN],h[NN],s[NN],dp[NN],lsh[NN],id[NN],t[NN],sum;
int Find(int x){
return lower_bound(lsh+1,lsh+1+lsh[0],x)-lsh;
}
int Nxt(int x){
return upper_bound(lsh+1,lsh+1+lsh[0],x)-lsh;
}
vector<pa> v[NN];
struct SegTr{
int mn_dp,mn_val;
#define mn_dp(x) sgt[x].mn_dp
#define mn_val(x) sgt[x].mn_val
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
}sgt[NN<<2];
void Up(int p){
mn_dp(p)=min(mn_dp(ls(p)),mn_dp(rs(p)));
mn_val(p)=min(mn_val(ls(p)),mn_val(rs(p)));
return;
}
void Prune(int pos,int val,int p=1,int L=1,int R=lsh[0]){
if(L==R){
mn_dp(p)=val;
mn_val(p)=val+lsh[L];
return;
}
int mid=L+R>>1;
if(pos<=mid)Prune(pos,val,ls(p),L,mid);
else Prune(pos,val,rs(p),mid+1,R);
return Up(p);
}
int Min_DP(int l,int r,int p=1,int L=1,int R=lsh[0]){
if(l>R||L>r)return INF;
if(l<=L&&R<=r)return mn_dp(p);
int mid=L+R>>1;
return min(Min_DP(l,r,ls(p),L,mid),Min_DP(l,r,rs(p),mid+1,R));
}
int Min_Val(int l,int r,int p=1,int L=1,int R=lsh[0]){
if(l>R||L>r)return INF;
if(l<=L&&R<=r)return mn_val(p);
int mid=L+R>>1;
return min(Min_Val(l,r,ls(p),L,mid),Min_Val(l,r,rs(p),mid+1,R));
}
multiset<int> ms[NN];
void Insert(int x){
ms[h[x]].insert(dp[x]);
Prune(h[x],*ms[h[x]].begin());
return;
}
void Erase(int x){
assert(ms[h[x]].count(dp[x]));
ms[h[x]].erase(ms[h[x]].find(dp[x]));
Prune(h[x],*ms[h[x]].begin());
return;
}
void CDQ(int l,int r){
if(l>=r)return;
int mid=l+r>>1;
CDQ(l,mid);
h[mid]=a[mid+1];
for(int i=mid-2;i>=l;i--)h[i]=max(h[i+1],a[i+1]);
h[mid+1]=a[mid+1];
for(int i=mid+2;i<=r;i++)h[i]=max(h[i-1],a[i]);
for(int i=mid+1;i<=r;i++)id[i]=i,t[i]=s[i];
sort(id+mid+1,id+r+1,[](int x,int y){return s[x]<s[y];});
sort(t+mid+1,t+r+1);
for(int i=l;i<=mid;i++){
int x=lower_bound(t+mid+1,t+r+1,s[i]-m)-t,
y=upper_bound(t+mid+1,t+r+1,s[i]+m)-t;
v[x].push_back({i,1});
v[y].push_back({i,-1});
}
for(int i=mid+1;i<=r;i++){
for(pa o:v[i]){
if(o.second==1)Insert(o.first);
else Erase(o.first);
}
int x=id[i];
int t1=Min_DP(1,h[x])+lsh[h[x]],t2=Min_Val(h[x]+1,n);
dp[x]=min(dp[x],min(t1,t2));
}
for(pa o:v[r+1]){
if(o.second==1)Insert(o.first);
else Erase(o.first);
}
for(int i=mid+1;i<=r+1;i++)v[i].clear();
CDQ(mid+1,r);
return;
}
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i],s[i]=s[i-1]+a[i],
lsh[++lsh[0]]=a[i];
if(a[i]>=0)sum+=a[i];
}
sort(lsh+1,lsh+1+lsh[0]);
lsh[0]=unique(lsh+1,lsh+1+lsh[0])-lsh-1;
for(int i=1;i<=n;i++)a[i]=Find(a[i]);
memset(dp,0x3f,sizeof dp);dp[0]=0;
for(int i=1;i<=lsh[0];i++)ms[i].insert(INF);
for(int i=1;i<=lsh[0];i++)Prune(i,INF);
CDQ(0,n);
if(dp[n]<=sum)cout<<dp[n];
else cout<<"Impossble!!!";
cerr<<dp[n];
return 0;
}

浙公网安备 33010602011771号