Codeforces 1295 E.Permutation Separation (线段树)

E.Permutation Separation

You are given a permutation p1,p2,…,pn (an array where each integer from 1 to n appears exactly once). The weight of the i-th element of this permutation is ai.

At first, you separate your permutation into two non-empty sets — prefix and suffix. More formally, the first set contains elements p1,p2,…,pk, the second — pk+1,pk+2,…,pn, where 1≤k<n.

After that, you may move elements between sets. The operation you are allowed to do is to choose some element of the first set and move it to the second set, or vice versa (move from the second set to the first). You have to pay ai dollars to move the element pi.

Your goal is to make it so that each element of the first set is less than each element of the second set. Note that if one of the sets is empty, this condition is met.

For example, if p=[3,1,2] and a=[7,1,4], then the optimal strategy is: separate p into two parts [3,1] and [2] and then move the 2-element into first set (it costs 4). And if p=[3,5,1,6,2,4], a=[9,1,9,9,1,9], then the optimal strategy is: separate p into two parts [3,5,1] and [6,2,4], and then move the 2-element into first set (it costs 1), and 5-element into second set (it also costs 1).

Calculate the minimum number of dollars you have to spend.

Input

The first line contains one integer n (2≤n≤2⋅105) — the length of permutation.

The second line contains n integers p1,p2,…,pn (1≤pi≤n). It’s guaranteed that this sequence contains each element from 1 to n exactly once.

The third line contains n integers a1,a2,…,an (1≤ai≤109).

Output

Print one integer — the minimum number of dollars you have to spend.

Examples
input

3
3 1 2
7 1 4

output

4

input

4
2 4 1 3
5 9 8 3

output

3

input

6
3 5 1 6 2 4
9 1 9 9 1 9

output

2

题意:
给一个长度为n的排列p[]
现在要把这个排列切成左右两部分
切完之后可以随意把一个部分的数移动到另外一个部分,移动p[i]的代价是a[i]
现在要让两部分中左边部分的所有数小于右边部分,任选切点和移动方式,问最小花费是多少
当有一边为空的时候也满足条件
思路:
先考虑一个最暴力的垃圾思路:
1.先枚举切点
2.枚举左边集合的最大值k(最大值为k的时候要把1-k都移动到左边,k-(n+1)都移动到右边)
3.对当前切点下的每个k的花费取min即为当前切点最小花费
4.对所有切点的最小花费取min即为答案

两层枚举就已经O(n^2),更别说还要计算花费,显然会超时,得想办法优化

最终解法:
1.枚举切点
2.ans[i]表示当前切点下,左边最大值为i的花费,所以min(ans[k])就是当前切点下的最小花费
ans[0]为左边为空的花费,ans[n]为右边为空的花费,因为题目说了空集也满足条件
3.考虑切点之间的联系,思考能否利用上一个切点的ans数组快速求出当前切点的ans数组.
可以发现:切点从左到右枚举的过程中,只有一个数从右边移动到了左边,假设这个数是x,
那么当前切点下,左边最大值为x-n的答案ans[x]-ans[n]需要减少a(a是移动x的花费),
因为当x已经在左边的时候,且左边集合最大值为x-n时,相比于上一个切点,已经把移动x到左边了,
所以把上一切点的ans[x]-ans[n]减去a,
同理,左边集合最大值为0-(x-1),相比于上一切点,这次需要把x移动到右边,所以ans[0]-ans[x-1]加上a
这样我们就能快速更新上一组的ans[]得到新的ans[]
4.对于每个切点ans[0]-ans[n]取min,即为当前切点的最小花费
5.对于每个切点的最小花费取min即为答案

因为步骤3之中只有区间加减法和区间min,因此可以用线段树来实现

现在已经知道如何用上一组的ans[]求出当前ans[],但是第一组的ans[]没有上一组
第一组的上一组即ans[]的初始状态,切点在最左边的时候(切完之后左边为空集)
因为左边是空集,所以这个状态下ans[i]为小于等于i的所有数的花费a的和
可以求出前缀和数组再建树,也可以直接对于每个x,利用线段树把ans[x]-ans[n]加上x的花费a
code:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxm=2e5+5;
ll laz[maxm<<2];
ll mi[maxm<<2];
int pos[maxm];
int p[maxm];
int a[maxm];
int n;
void pushup(int node){
    mi[node]=min(mi[node*2],mi[node*2+1]);
}
void pushdown(int node){
    if(laz[node]){
        laz[node*2]+=laz[node];
        laz[node*2+1]+=laz[node];
        mi[node*2]+=laz[node];
        mi[node*2+1]+=laz[node];
        laz[node]=0;
    }
}
void update(int st,int ed,int val,int l,int r,int node){//区间加法
    if(st<=l&&ed>=r){
        laz[node]+=val;
        mi[node]+=val;
        return ;
    }
    pushdown(node);
    int mid=(l+r)/2;
    if(st<=mid){
        update(st,ed,val,l,mid,node*2);
    }
    if(ed>=mid+1){
        update(st,ed,val,mid+1,r,node*2+1);
    }
    pushup(node);
}
signed main(){
    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++){//初始状态为ans[i]=小于等于i的花费a的和
        update(p[i],n,a[i],0,n,1);
    }
    ll ans=1e18;
    for(int i=1;i<n;i++){//切点不能在n的后面,所以只循环到n-1
        update(p[i],n,-a[i],0,n,1);
        update(0,p[i]-1,a[i],0,n,1);
        ans=min(ans,mi[1]);
    }
    cout<<ans<<endl;
    return 0;
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值