线段树,思维——cf1295E

/*
给定一个数组,切割成pre,suf,现在要移动这两个集合中的元素,使 Max(pre)<Min(suf),且移动代价最小 
对问题进行转化:移动后长度为len的pre,必定包含[1,len]
    所以枚举移动前pre的长度,再考虑每个移动后pre长度的对应的代价
        第二步可用线段树优化,每个结点维护移动后长度为i的对应的代价,可以通过区间更新进行递推维护
    求出每种pre的最小值,再求个最小值即可 
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=200005;
const ll INF=(1LL<<60)-1;
int p[MAXN],a[MAXN];
ll mi[MAXN<<2],tag[MAXN<<2];
inline void push_up(int n)
{
    mi[n]=min(mi[n<<1],mi[n<<1|1]);
}
inline void push_down(int n)
{
    if(!tag[n])return;
    mi[n<<1]+=tag[n];
    mi[n<<1|1]+=tag[n];
    tag[n<<1]+=tag[n];
    tag[n<<1|1]+=tag[n];
    tag[n]=0;
}
void update(int ql,int qr,int v,int l,int r,int n)
{
    if(ql==l && qr==r)
    {
        mi[n]+=v;
        tag[n]+=v;
        return;
    }
    push_down(n);
    int m=(l+r)/2;
    if(qr<=m)update(ql,qr,v,l,m,n<<1);
    else if(ql>m)update(ql,qr,v,m+1,r,n<<1|1);
    else
    {
        update(ql,m,v,l,m,n<<1);
        update(m+1,qr,v,m+1,r,n<<1|1);
    }
    push_up(n);
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&p[i]);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
        update(p[i],n,a[i],0,n,1);
    ll res=INF;
    for(int i=1;i<n;i++)
    {
        update(p[i],n,-a[i],0,n,1);
        update(0,p[i]-1,a[i],0,n,1);
        res=min(res,mi[1]);
    }
    return 0*printf("%lld
",res);
}
原文地址:https://www.cnblogs.com/zsben991126/p/12262668.html