本文版权归博客园,洛谷博客和蒟蒻wjr所有,欢迎转载,但需保留此段声明,并给出原文链接,如有侵权行为,还请不吝啬向博主举报,谢谢合作。
开一波E题闷声发大财
感谢@Karry5307 在考后提供思路
给定一个序列(p)(为1~n的排列),和这个序列每一个数所对应的花费a以及序列的长度n。
取一数(1leq k<n),将(p)序列分成两个序列,分别是(p_1,p_2,...,p_k)和(p_{k+1},p_{k+2},...,p_n)
将两个序列的某些数移到另一个序列使得第一个序列的所有数小于第二个序列的所有数,注意如果其中一组为空,则满足此条件,移动(p_i)的花费是(a_i)
求最小花费
一道裸的线段树
考虑维护(a_i)的前缀和(sum_i=sum_{j=1}^{i}a_i),因为(1leq k<n),所以前缀和(sum)最多只要维护到(a_{n-1})即可
假设第一个序列的所有数都移到了第二个序列值最小虽然不大可能则
答案是(sum)中的最小值
因为(p)序列是1~n的排列,所以记(pos_i)为(i)在序列(p)中出现的位置
枚举i从1~n
假设将(p[pos_i])最终在第一序列,那么(p[pos_1],p[pos_2],...,,p[pos_{i-1}]一定在最终在第一序列),考虑当前对前缀和的修改。
当(k>=pos_i)时,(p[pos_i])在第一序列,那么不需要费用
当(k<pos_i)时,(p[pos_i])在第二序列,需要(a[pos_i])的费用移动到第一序列
则将(sum_1)~(sum_{pos_i-1})的值分别加上(a[pos_i])
(sum_{pos_i})~(sum_{n-1})的值分别减去(a[pos_i])
当前策略最小值即当前(sum)的最小值。
答案即是对所有策略的最小值取最小值
#include <cstdio>
#include <algorithm>
#define re register
#define ll long long
#define lc (root<<1)
#define rc (root<<1|1)
using namespace std;
template<typename T>
inline void read(T&x)
{
x=0;
char s=(char)getchar();
bool flag=false;
while(!(s>='0'&&s<='9'))
{
if(s=='-')
flag=true;
s=(char)getchar();
}
while(s>='0'&&s<='9')
{
x=(x<<1)+(x<<3)+s-'0';
s=(char)getchar();
}
if(flag)
x=(~x)+1;
return;
}
const int N=2e5+5;
struct Tree
{
ll minn,lazy;
} tree[N<<2];
ll sum[N];
inline void build(int root,int l,int r)
{
if(l==r)
{
tree[root].minn=sum[l];
tree[root].lazy=0;
return;
}
int mid=(l+r)>>1;
build(lc,l,mid);
build(rc,mid+1,r);
tree[root].minn=min(tree[lc].minn,tree[rc].minn);
return;
}
inline void pushdown(int root)
{
if(!tree[root].lazy)
return;
tree[lc].minn+=tree[root].lazy;
tree[rc].minn+=tree[root].lazy;
tree[lc].lazy+=tree[root].lazy;
tree[rc].lazy+=tree[root].lazy;
tree[root].lazy=0;
return;
}
inline void change(int root,int l,int r,int x,int y,int val)
{
if(r<x||l>y)
return;
if(x<=l&&r<=y)
{
tree[root].minn+=val;
tree[root].lazy+=val;
return;
}
int mid=(l+r)>>1;
pushdown(root);
change(lc,l,mid,x,y,val);
change(rc,mid+1,r,x,y,val);
tree[root].minn=min(tree[lc].minn,tree[rc].minn);
return;
}
int n,p[N],a[N],pos[N];
ll ans;
int main()
{
read(n);
for(re int i=1; i<=n; ++i)
{
read(p[i]);
pos[p[i]]=i;
}
for(re int i=1; i<=n; ++i)
{
read(a[i]);
sum[i]=sum[i-1]+a[i];
}
build(1,1,n-1);
ans=tree[1].minn;
for(re int i=1; i<=n; ++i)
{
change(1,1,n-1,1,pos[i]-1,a[pos[i]]);
change(1,1,n-1,pos[i],n-1,-a[pos[i]]);
// printf("%d
",ans);
ans=min(ans,tree[1].minn);
}
printf("%lld
",ans);
return 0;
}