CodeForces_1295E Permutation Separation(线段树)

Permutation Separation

time limit per test:2 seconds
memory limit per test:256 megabytes
Problem Description

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 1k <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 (2n2⋅105) — the length of permutation.

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

The third line contains n integers a1,a2,…,an (1ai109).

Output

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

Sample Input

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

Sample Output

2

题意

有一个排列,第i位数字为ai.初始可以选择一个位置,这个位置及其左边元素为左集合,这个位置右边的元素为右集合。可以花费一定的代价,将一个元素转移至另一个集合中,转移第i个元素的代价为pi,要求左集合的所有值小于右集合的所有值。求需要的最小代价?

题解:

因为可以任意划分,所以初始集合不确定,需要单独考虑每种划分的最优解。对于每个初始的划分可以考虑依次考虑将所有 ≤ i \le i i的数都留在左集合, > i > i >i的都留在右集合( i ∈ [ 1 , n ] i\in[1,n] i[1,n]),求代价取最小值即可。当求 i 的代价时,需要计算两部分,将左集合中 [ i + 1 , n ] [i+1,n] [i+1,n]的数转移,将右集合 [ 1 , i ] [1,i] [1,i]的数转移的代价和。

可以考虑利用线段树维护所有 i 的代价最小值。初始划分左区间为空,右区间满的,则初始状态下第 i 个数的值就是移动数字1~数字i的所有数字的代价和(是数字1到i,不是位置,左集合为空,所以不需要计算左集合的贡献)。当初始划分将 aj 分至左集合,则对于上一种划分来说,右集合少了 aj ,所以所有的 i ≥ a j i \ge a_j iaj来说,不用再花费pj的代价将aj移动至左集合(因为他已经被移动至右集合了),但对于左集合来说,所有的 i < a j i<a_j i<aj,需要花费额外pj的代价将aj移动至右集合。

所以每次更新需要将1 ~ aj-1 的部分加上pj,aj ~ n的部分减去pj

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<ctype.h>
#include<cstring>
#include<map>
#include<queue>
#include<stack>
#include<iterator>
#define dbg(x) cout<<#x<<" = "<<x<<endl;
#define INF 0x3f3f3f3f
#define eps 1e-6

using namespace std;
typedef long long LL;
typedef pair<int, int> P;
const int maxn = 200200;
const int mod = 1000000007;
LL ans, sum[maxn], p[maxn*4], lz[maxn*4];
int a[maxn], b[maxn];
void creat(int l, int r, int k);
void pushdown(int k);
void Update(int l, int r, int al, int ar, int x, int k);

int main()
{
	int n, i, j, k;
	scanf( "%d", &n);
	ans = 1e17;
	for(i=1;i<=n;i++)
		scanf("%d", &a[i]);
	for(i=1;i<=n;i++){
		scanf("%d", &b[i]);
		sum[a[i]] = b[i];
	}
	for(i=2;i<=n;i++){
		sum[i] += sum[i-1];
	}
	creat(1, n, 1);
	//另左右集合为空的代价
	ans = min(b[1], b[n]);
	for(i=1;i<n;i++){
		Update(1, n, 1, a[i]-1, b[i], 1);
		Update(1, n, a[i], n, -b[i], 1);
		ans = min(ans, p[1]);
	}
	printf("%I64d\n", ans);
	return 0;
}

void creat(int l, int r, int k)
{
	lz[k] = 0;
	if(l == r){
		p[k] = sum[l];
		return;
	}
	int mid = (l+r)/2;
	creat(l, mid, 2*k);
	creat(mid+1, r, 2*k+1);
	p[k] = min(p[2*k], p[2*k+1]);
}

void Update(int l, int r, int al, int ar, int x, int k)
{
	if(al>ar)return;
	if(l == al && r == ar){
		p[k] += x;
		lz[k] += x;
		return;
	}
	pushdown(k);
	int mid = (l+r)/2;
	if(ar <= mid)Update(l, mid, al, ar, x,2*k);
	else if(al > mid)Update(mid+1, r, al, ar, x, 2*k+1);
	else Update(l, mid, al, mid, x, 2*k), Update(mid+1, r, mid+1, ar, x, 2*k+1);
	p[k] = min(p[2*k], p[2*k+1]);
}

void pushdown(int k)
{
	if(lz[k])
	{
		p[2*k]+=lz[k];
		p[2*k+1] += lz[k];
		lz[2*k] += lz[k];
		lz[2*k+1] += lz[k];
		lz[k] = 0;
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值