[CF1295E] Permutation Separation

Description

给定一个长度为 (n) 的排列,你可以将它切成两段 (A,B),分别作为两个集合,对于第 (i) 个元素,可以花费 (a[i]) 的代价把它移动到对面的集合中。求至少花费多少的代价,才能使得一个集合中的任意元素比另一个集合中的任意元素小。

Solution

枚举初态分界点 (pos) 和分界值 (val),设此时的答案为 (f(pos,lim)),暴力计算的话复杂度为 (O(n^2))

考虑到 (f(pos,lim)-f(pos-1,lim)=sum_{lim < p_{pos}} a_{pos} - sum_{lim ge p_{pos}} a_{pos}),我们用线段树来维护对于某个确定的 (pos) 的所有 (f(pos,lim)),每次修改 (pos) 时,只需要将 (1 le lim < p_{pos}) 部分 (+a_{pos}),将 (p_{pos} le lim le n) 部分 (-a_{pos})。区间修改,区间询问最小值。

特别注意,lim 是可以 =0 的。

#include <bits/stdc++.h>
using namespace std;

#define int long long 
const int N = 1000005;

struct SegmentTree 
{
    int a[N],tag[N];
    void Pushup(int p)
    {
        a[p]=min(a[p*2],a[p*2+1]);
    }
    void Put(int p,int val)
    {
        a[p]+=val;
        tag[p]+=val;
    }
    void Pushdown(int p)
    {
        if(tag[p])
        {
            Put(p*2,tag[p]);
            Put(p*2+1,tag[p]);
            tag[p]=0;
        }
    }
    void Modify(int p,int l,int r,int ql,int qr,int val)
    {
        if(l>qr||r<ql) return;
        if(l>=ql&&r<=qr)
        {
            Put(p,val);
        }
        else 
        {
            Pushdown(p);
            Modify(p*2,l,(l+r)/2,ql,qr,val);
            Modify(p*2+1,(l+r)/2+1,r,ql,qr,val);
            Pushup(p);
        }
    }
    int Query()
    {
        return a[1];
    }
} seg;

int n,a[N],b[N],p[N],f[N];

signed main()
{
    ios::sync_with_stdio(false);

    cin>>n;
    for(int i=1;i<=n;i++) cin>>p[i];
    for(int i=1;i<=n;i++) cin>>a[i];

    for(int i=1;i<=n;i++) 
    {
        // cerr<<"Modify "<<p[i]<<" "<<n<<" "<<a[i]<<endl;
        seg.Modify(1,0,n,p[i],n,a[i]);
    }
    int ans=1e18;
    for(int i=1;i<n;i++)
    {
        seg.Modify(1,0,n,0,p[i]-1,a[i]);
        seg.Modify(1,0,n,p[i],n,-a[i]);
        ans=min(ans,seg.Query());
    }
    cout<<ans<<endl;
}

原文地址:https://www.cnblogs.com/mollnn/p/14037993.html