题目链接:http://codeforces.com/contest/1295/problem/E
题意:给定一个排列,以及每个位置的价值a[]。现在把当前排列分成前后两部分,可以将左边集合的数扔到右边集合,但需要消费该数对应的代价;同理可以将右边集合的数扔到左边集合,但需要消费该数对应的代价。通过若干次移动操作,实现左边集合数恒小于右边集合数。求最小花费。
题解:最终实现左边集合数恒小于右边集合数,等价于左边数
<
v
a
l
<val
<val,右边数
>
=
v
a
l
>=val
>=val,对于当前选定的
v
a
l
val
val,我们判断每个划分位置
p
o
s
pos
pos,我们将
<
=
p
o
s
<=pos
<=pos的数放在左集合,将
>
p
o
s
>pos
>pos的数放在右集合。那么对于
>
p
o
s
,
<
v
a
l
>pos,<val
>pos,<val的数需要从右集合扔到左集合;对于
<
=
p
o
s
,
>
=
v
a
l
<=pos,>=val
<=pos,>=val的数需要从左集合扔到右集合。
用数组
m
n
[
]
mn[]
mn[]来表示对于当前
v
a
l
val
val,下标划定为
p
o
s
pos
pos需要的最小代价
m
n
[
p
o
s
]
mn[pos]
mn[pos],从0到n枚举
v
a
l
val
val,每次增加1,重点考究每次增加1时
m
n
[
]
mn[]
mn[]的更新,可以发现,当
v
a
l
val
val增加为
v
a
l
+
1
val+1
val+1后,原本
>
=
p
o
s
i
t
i
o
n
[
v
a
l
]
>=position[val]
>=position[val]的位置
m
n
[
]
mn[]
mn[]都减少代价
a
[
p
o
s
i
t
i
o
n
]
a[position]
a[position],
<
p
o
s
i
t
i
o
n
[
v
a
l
]
<position[val]
<position[val]的位置增加了代价
a
[
p
o
s
i
t
i
o
n
]
a[position]
a[position]。该过程可以用线段树实现。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f3f3f3f3f
const int maxn=200010;
ll mn[maxn<<2],lazy[maxn<<2];
int a[maxn],n;
ll b[maxn];
int pos[maxn];
void pushup(int rt){
mn[rt]=min(mn[rt<<1],mn[rt<<1|1]);
}
void pushdown(int rt,int l,int r){
lazy[rt<<1]+=lazy[rt];
lazy[rt<<1|1]+=lazy[rt];
int mid=(l+r)>>1;
mn[rt<<1]+=lazy[rt];
mn[rt<<1|1]+=lazy[rt];
lazy[rt]=0;
}
void build(int rt,int l,int r){
lazy[rt]=0;
if(l==r){
mn[rt]=b[l];return;
}
int mid=(l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
pushup(rt);
}
void update(int rt,int l,int r,int a,int b,int val){
if(l>=a&&r<=b){
mn[rt]+=val;
lazy[rt]+=val;
return;
}
if(lazy[rt]) pushdown(rt,l,r);
int mid=(l+r)>>1;
if(a<=mid) update(rt<<1,l,mid,a,b,val);
if(mid<b) update(rt<<1|1,mid+1,r,a,b,val);
pushup(rt);
}
ll query(int rt,int l,int r,int a,int b){
if(l>=a&&r<=b){
return mn[rt];
}
if(lazy[rt]) pushdown(rt,l,r);
int mid=(l+r)>>1;
ll ans=inf;
if(a<=mid) ans=min(ans,query(rt<<1,l,mid,a,b));
if(mid<b) ans=min(ans,query(rt<<1|1,mid+1,r,a,b));
return ans;
}
int main(){
scanf("%d",&n);
for(int i=1,p;i<=n;i++){
scanf("%d",&p);
pos[p]=i;
}
b[0]=a[0]=0;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
b[i]=b[i-1]+a[i];
}
build(1,1,n-1);
ll ans=query(1,1,n-1,1,n-1);
for(int i=1;i<=n;i++){
int p=pos[i];
if(p<n)
update(1,1,n-1,p,n-1,-a[p]);
if(p>1)
update(1,1,n-1,1,p-1,a[p]);
ans=min(ans,query(1,1,n-1,1,n-1));
}
printf("%I64d\n",ans);
}