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号
浙公网安备 33010602011771号