https://vjudge.net/problem/CodeForces-1295E
题目大意:
p
1
,
p
2
…
…
p
n
p_1,p_2……p_n
p1,p2……pn为
n
n
n的某个全排列,
a
i
a_i
ai为移动
p
i
p_i
pi所需要的花费,你可以选取任意
k
,
1
<
=
k
<
n
k,1<=k<n
k,1<=k<n,把序列
p
p
p分成两部分,即
p
1
,
…
…
,
p
k
p_1,……,p_k
p1,……,pk和
p
k
+
1
,
…
…
,
p
n
p_{k+1},……,p_n
pk+1,……,pn。然后你可以做如下操作,即每次从任意一个序列中选取某个元素移动到另一个序列中,最终你需要使得第一个序列中的任意一个元素小于第二个序列中的任意一个元素(即前者中的最大值小于后者中的最小值),问最小花费是多少(如果这两个序列中任意一个序列为空,则认为该方案也符合题意)。
思路:主要是转换这一步比较难想。设值
v
a
l
val
val满足第一个序列中的元素
<
v
a
l
<val
<val,第二个序列中的元素
>
=
v
a
l
>=val
>=val,由于
p
p
p是
n
n
n的全排列,那么
v
a
l
val
val的取值范围为
[
1
,
n
+
1
]
[1,n+1]
[1,n+1](包含了某个序列为空的情况)。假设
v
a
l
val
val为一定值,
t
[
p
o
s
]
t[pos]
t[pos]表示选取
k
=
p
o
s
k=pos
k=pos时所需的花费,那么显然
t
[
p
o
s
]
t[pos]
t[pos]由两部分组成:
c
o
s
t
1
=
∑
a
i
其
中
1
<
=
i
<
=
p
o
s
&
&
p
i
>
=
v
a
l
cost_1=\sum a_i\ \ 其中1<=i<=pos\ \&\&\ p_i>=val
cost1=∑ai 其中1<=i<=pos && pi>=val
c
o
s
t
2
=
∑
a
i
其
中
p
o
s
<
i
<
=
n
&
&
p
i
<
v
a
l
cost_2=\sum a_i\ \ 其中pos<i<=n\ \&\&\ p_i<val
cost2=∑ai 其中pos<i<=n && pi<val
很明显,当
v
a
l
val
val为定值时,
t
[
1
…
…
n
]
t[1……n]
t[1……n]可通过递推计算出,复杂度为
O
(
n
)
O(n)
O(n)。然而
1
<
=
v
a
l
<
=
n
+
1
1<=val<=n+1
1<=val<=n+1,所以暴力的复杂度为
O
(
n
2
)
O(n^2)
O(n2),显然不行。那么我们思考一下当
v
a
l
val
val增加
1
1
1时,数组
t
t
t的变化情况,首先看下图:
不难发现,当
v
a
l
val
val增加
1
1
1时,第一部分中值为
v
a
l
val
val的元素不用移到第二部分了,且第二部分中值为
v
a
l
val
val的元素需要移到第一部分,不妨设
p
[
i
]
=
v
a
l
p[i]=val
p[i]=val,那么也就是说
t
[
i
…
…
n
]
t[i……n]
t[i……n]的花费可以减少
a
[
t
]
a[t]
a[t],
t
[
1
…
…
i
−
1
]
t[1……i-1]
t[1……i−1]的花费需要增加
a
[
t
]
a[t]
a[t],而我们所求的最优解
=
M
i
n
(
t
[
1
…
…
n
−
1
]
)
=Min(t[1……n-1])
=Min(t[1……n−1]),区间修改、查询最小值……不就是线段树模板题吗!
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn=2e5+5;
struct Tree
{
int l,r;
ll lazy,MIN;
}tree[maxn<<2];
int n;
int a[maxn],p[maxn],idx[maxn];
ll t[maxn];
inline void up(int i)
{
tree[i].MIN=min(tree[i<<1].MIN,tree[i<<1|1].MIN);
}
inline void down(int i)
{
int l=i<<1,r=i<<1|1;
tree[l].lazy+=tree[i].lazy,tree[r].lazy+=tree[i].lazy;
tree[l].MIN+=tree[i].lazy,tree[r].MIN+=tree[i].lazy;
tree[i].lazy=0;
}
void build(int i,int l,int r)
{
tree[i].l=l,tree[i].r=r,tree[i].lazy=0;
if(l==r)
{
tree[i].MIN=t[l];
return ;
}
int mid=l+r>>1;
build(i<<1,l,mid);
build(i<<1|1,mid+1,r);
up(i);
}
void update(int i,int l,int r,int v)
{
if(tree[i].l==l&&tree[i].r==r)
{
tree[i].MIN+=v,tree[i].lazy+=v;
return ;
}
if(tree[i].lazy)
down(i);
int mid=tree[i].l+tree[i].r>>1;
if(mid>=r)
update(i<<1,l,r,v);
else if(mid<l)
update(i<<1|1,l,r,v);
else
update(i<<1,l,mid,v),update(i<<1|1,mid+1,r,v);
up(i);
}
ll query(int i,int l,int r)
{
if(tree[i].l==l&&tree[i].r==r)
return tree[i].MIN;
if(tree[i].lazy)
down(i);
int mid=tree[i].l+tree[i].r>>1;
if(mid>=r)
return query(i<<1,l,r);
else if(mid<l)
return query(i<<1|1,l,r);
else
return min(query(i<<1,l,mid),query(i<<1|1,mid+1,r));
up(i);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&p[i]),idx[p[i]]=i;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
t[i]=t[i-1]+a[i];
}
build(1,1,n);
int val=1,id;
ll ans=query(1,1,n-1);
while(val<=n)
{
id=idx[val];
update(1,id,n,-a[id]);
if(id>1)
update(1,1,id-1,a[id]);
ans=min(ans,query(1,1,n-1));
++val;
}
printf("%lld\n",ans);
return 0;
}