例题:E. Permutation Separation
代码:
参考博客
题目大意:给你一个排列,每个排列中的第i个数字为pi ,权值为wi 。你可以选择一个位置切一刀把它分成左右两部分,然后你可以花费wi把pi 移动到另一个部分。最终要保证左半部分的最大值小于右半部分的最小值。注意,切分时不可以有空集,但移动后可以有。
详细思路
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<string>
#include<map>
#include<queue>
using namespace std;
#define ll long long
#define lowbit(x) ((x)&(-(x)))
#define mid ((l+r)>>1)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
using namespace std;
const int maxn = 2e5 + 50;
int p[maxn];
ll w[maxn];
ll lz[maxn<<2], mi[maxn<<2];
void down(int rt){
lz[rt<<1] += lz[rt];
mi[rt<<1] += lz[rt];
lz[rt<<1|1] += lz[rt];
mi[rt<<1|1] += lz[rt];
lz[rt] = 0;
return;
}
void up(int rt){
mi[rt] = min(mi[rt<<1], mi[rt<<1|1]);
}
void update(int rt, int l, int r, int L, int R, ll x){
if(L <= l && r <= R) {
lz[rt] += x;
mi[rt] += x;
return;
}down(rt);
if(L <= mid) update(lson, L, R, x);
if(R > mid) update(rson, L, R, x);
up(rt);
}
int n;
ll sol(){
for(int i = 1; i < n; ++i){
update(1, 0, n, 0, p[i], w[i]);
}//初始为所有数都在左边
if(p[n] != n) update(1, 0, n, p[n]+1, n, w[n]);
//这一行相当于简化了步骤,在n-1和n之间切
ll ans = mi[1];//mi[i]代表右边最小为i时的最小花费
for(int i = n-1; i > 1; --i){
if(p[i] != n) update(1, 0, n, p[i]+1, n, w[i]);
//把i移动到右边,则所有mi[p>i]都要加w[i]
update(1, 0, n, 0, p[i], -w[i]);
//把i移动到右边,则所有mi[p<=i]都要加w[i]
ans = min(ans, mi[1]);
}return ans;
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; ++i){
scanf("%d", &p[i]);
}
for(int i = 1; i <= n; ++i) scanf("%lld", &w[i]);
ll ans = sol();
ans = min(w[1], ans);//移动后为空集
ans = min(w[n], ans);
cout<<ans<<endl;
return 0;
}
一道对比题:E. The Contest
原题地址
题目大意:三个序列,规则和上题一样,但移动的权值都为1,问最少权值。
直接把三个序列排好序后拼起来,然后求最长上升序列。。。
qsc讲解