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;
}