qoj8542 Add One 2 题解

题目链接

点击打开链接

题目解法

我们先考虑什么样的序列 \(x_1,...,x_n\) 是可以被得到的
对于 \(x_i>x_{i+1}\) 的位置,我们需要至少对前缀 \([1,i]\)\(x_i-x_{i+1}\) 次操作;对于 \(x_{i-1}<x_{i}\) 的位置,我们需要至少对后缀 \([i,n]\)\(x_i-x_{i-1}\) 次操作
我们需要最大利用这个性质,所以我们从后往前依次做前缀操作,再从前往后依次做后缀操作,这样操作完之后会得到 \(x_i\) 全部相同的序列,则当前序列合法的条件是操作完后序列中的数 \(\ge 0\)
形式化表示一下为:\(x_1-\sum\limits_{i=1}^{n-1}[x_i>x_{i+1}](x_i-x_{i+1})\ge 0\)(或 \(x_n-\sum\limits_{i=1}^{n-1}[x_i<x_{i+1}](x_{i+1}-x_i)\ge 0\),这两个式子是等价的)

\(x_0=x_{n+1}=inf\)
即条件为 \(\sum\limits_{i=0}^{n}|x_i-x_{i+1}|\le 2\times inf\)(就是把上面的限制翻一倍)

我们考虑在原来的序列上单点加,使其满足条件,要求最小化序列和
可以发现,将满足 \(\max\limits_{i=l}^r\{x_i\}<\min\{x_{l-1},x_{r+1}\}\)\([l,r]\) 全部 \(+1\),可以使左式的值 \(-2\)
用这个思路,我们考虑建出笛卡尔树,只要按照区间长度从小往大贪心做即可

时间复杂度 \(O(n\log n)\)

#include <bits/stdc++.h>
#define F(i,x,y) for(int i=(x);i<=(y);i++)
#define DF(i,x,y) for(int i=(x);i>=(y);i--)
#define ms(x,y) memset(x,y,sizeof(x))
#define SZ(x) (int)x.size()-1
#define all(x) x.begin(),x.end()
#define pb push_back
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
typedef pair<int,int> pii;
template<typename T> void chkmax(T &x,T y){ x=max(x,y);}
template<typename T> void chkmin(T &x,T y){ x=min(x,y);}
template<typename T> void read(T &FF){
    FF=0;int RR=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
    for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
    FF*=RR;
}
const int N=1000010;
const LL inf=1e17;
int n,L[N],R[N];
LL a[N];
pii seq[N];
int stk[N],top;
void work(){
    read(n);
    F(i,1,n) read(a[i]);
    a[0]=a[n+1]=inf;
    LL tot=0;F(i,0,n) tot+=abs(a[i]-a[i+1]);
    stk[top=0]=-1;
    F(i,0,n+1){
        while(top&&a[i]>=a[stk[top]]) top--;
        L[i]=stk[top]+1,stk[++top]=i;
    }
    stk[top=0]=n+2;
    DF(i,n+1,0){
        while(top&&a[i]>a[stk[top]]) top--;
        R[i]=stk[top]-1,stk[++top]=i;
    }
    int cnt=0;
    F(i,1,n){
        if(L[i]==1&&R[i]==n) continue;
        if(L[i]>1&&(R[i]==n||R[L[i]-1]==R[i])) seq[++cnt]={R[i]-L[i]+1,a[L[i]-1]-a[i]};
        else seq[++cnt]={R[i]-L[i]+1,a[R[i]+1]-a[i]};
    }
    sort(seq+1,seq+cnt+1);
    LL ans=0;
    F(i,1,cnt){
        if(tot<=2*inf) break;
        auto [x,y]=seq[i];
        if(tot-2*y<=2*inf) ans+=((tot-2*inf)/2)*x,tot=2*inf;
        else ans+=1ll*x*y,tot-=2*y;
    }
    F(i,1,n) ans+=a[i];
    printf("%lld\n",ans);
}
int main(){
    int T;read(T);
    while(T--) work();
    return 0;
}

posted @ 2024-06-22 16:44  Farmer_D  阅读(144)  评论(0)    收藏  举报