[Codeforces 1295E]Permutation Separation(线段树+贪心)

[Codeforces 1295E]Permutation Separation(线段树+贪心)

题面

给出一个排列\(p_1,p_2,...p_n\).初始时你需要选择一个位置把排列分成左右两个。然后在两个序列间移动元素使得左边序列的所有元素都比右边的所有元素小。给出每个元素\(p_i\)从一个序列移动到另一个序列的代价\(a_i\).

分析

显然最后得到的序列是小的数在一边,大的数在另一边。设从值为\(i\)的元素处分开之后移动代价为\(ans_i\). 一开始假设所有数都移到右边序列,那么\(ans_i\)就是值\(\leq i\)的元素的代价之和。然后从1~n枚举分割点\(i\)。对于\(p_i\),它放到左边之后\(ans[p_i+1,n]\)都需要去掉代价(因为\(p_i\)一开始就在左边,不需要移动)\(a_i\).而\(ans[0,p_i-1]\)需要增加代价\(a_i\).当前答案就是所有ans的最小值.

用线段树维护ans数组,时间复杂度为\(O(n\log n)\)

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#define INF 0x3f3f3f3f3f3f3f3f
#define maxn 200000
using namespace std;
typedef long long ll;
struct segment_tree{
    struct node{
        int l;
        int r;
        ll val;
        ll mark;
    }tree[maxn*4+5];
    void push_up(int pos){
        tree[pos].val=min(tree[pos<<1].val,tree[pos<<1|1].val);
    }
    void build(int l,int r,int pos){
        tree[pos].l=l;
        tree[pos].r=r;
        if(l==r) return;
        int mid=(l+r)>>1;
        build(l,mid,pos<<1);
        build(mid+1,r,pos<<1|1);
    }
    void add_tag(int pos,ll mark){
        tree[pos].val+=mark;
        tree[pos].mark+=mark;
    }
    void push_down(int pos){
        if(tree[pos].mark){
            add_tag(pos<<1,tree[pos].mark);
            add_tag(pos<<1|1,tree[pos].mark);
            tree[pos].mark=0;
        }
    }
    void update(int L,int R,int val,int pos){
        if(L<=tree[pos].l&&R>=tree[pos].r){
            add_tag(pos,val);
            return;
        }
        push_down(pos);
        int mid=(tree[pos].l+tree[pos].r)>>1;
        if(L<=mid) update(L,R,val,pos<<1);
        if(R>mid) update(L,R,val,pos<<1|1);
        push_up(pos);
    }
    inline ll get_min(){
        return tree[1].val;
    }
}T;

int n;
int p[maxn+5],a[maxn+5];
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&p[i]);
    }
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    T.build(0,n,1);//线段树的区间[i,i]表示开始在值i位置分开的答案ans[i]
    for(int i=1;i<=n;i++){//一开始假设所有数都移到右边,那么ans[i]就是p的前缀和sum[i]
        T.update(p[i],n,a[i],1);
    }
    ll ans=INF;
    for(int i=1;i<n;i++){//贪心,把小的数放到左边,枚举哪些数要放
        T.update(p[i],n,-a[i],1);//去掉p[i]从右到左的代价
        T.update(0,p[i]-1,a[i],1);
        ans=min(ans,T.get_min());
    }
    printf("%I64d\n",ans);
}

原文地址:https://www.cnblogs.com/birchtree/p/12245049.html

时间: 2024-10-04 08:57:06

[Codeforces 1295E]Permutation Separation(线段树+贪心)的相关文章

[CSP-S模拟测试]:Permutation(线段树+拓扑排序+贪心)

题目描述 你有一个长度为$n$的排列$P$与一个正整数$K$你可以进行如下操作若干次使得排列的字典序尽量小对于两个满足$|i−j|\geqslant K$且$|P_i−P_j|=1$的下标$i$与$j$,交换$P_i$与$P_j$ 输入格式 第一行包括两个正整数$n$与$K$第二行包括$n$个正整数,第$i$个正整数表示$P_i$ 输出格式 输出一个新排列表示答案输出共$n$行,第$i$行表示$P_i$ 样例 样例输入: 8 34 5 7 8 3 1 2 6 样例输出: 12675348 数据范

Codeforces 46D Parking Lot(线段树)

题目链接:Codeforces 46D Parking Lot 题目大意:一个街道,长为N,每辆车停进来的时候必须和前面间隔B米,和后面间隔F米,现在用两种操作,1是停进来一个长为x的车,2是第x辆车开走. 解题思路:区间合并,建一颗长度为N + B + F的线段树,然后每次停车进去的时候都查询x + B + F的区间,然后修改的时候只修改x的长度. #include <cstdio> #include <cstring> #include <algorithm> us

【Luogu】P1607庙会班车Fair Shuttle(线段树+贪心)

我不会做贪心题啊--贪心题啊--题啊--啊-- 我真TM菜爆了啊-- 这题就像凌乱的yyy一样,把终点排序,终点相同的按起点排序.然后维护一个查询最大值的线段树.对于一个区间[l,r],如果这个区间已经有的最大值为s,那么这个区间最多还能装下c-s头奶牛. 当然奶牛数量没那么多的话我也是没有办法 最后说一句,奶牛到终点就下车了,可以给别的奶牛腾空间,不计入个数.所以奶牛在车上的区间为[l,r-1]. #include <cstdio> #include <iostream> #in

Codeforces 482B Interesting Array(线段树)

题目链接:Codeforces 482B Interesting Array 题目大意:给定一个长度为N的数组,现在有M个限制,每个限制有l,r,q,表示从a[l]~a[r]取且后的数一定为q,问是 否有满足的数列. 解题思路:线段树维护,每条限制等于是对l~r之间的数或上q(取且的性质,相应二进制位一定为1),那么处理完所有的 限制,在进行查询,查询对应每个l~r之间的数取且是否还等于q.所以用线段树维护取且和,修改为或操作. #include <cstdio> #include <c

Codeforces 755D(思维+线段树)

http://codeforces.com/problemset/problem/755/D 从X到X+k点,其实只要求从X到X+k之间的点有多少条线超过X--X+K这条线就行,一开始直接暴力,就时间超时了,而用线段树维护就快多了. 1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long LL; 4 #define N 1000010 5 #define INF 0x3f3f3f3f 6 #define lso

APIO 2012 守卫 | 差分 / 线段树 + 贪心

题目:luogu 3634 首先把为 0 的区间删去,重新标号,可以差分也可以线段树. 把包含其他线段的线段删去,原因 1 是它没有用,原因 2 下面再说.然后,贪心选取最少的点来满足所有线段,即选取还没有点在上面的线段的右端点.如下图中选取的红色方格. 倘若不删去包含其他线段的线段,如上图中的蓝色虚线,我们在贪心选取点的时候,就会先扫到蓝线的左端点而后扫到第二条红线,按照规则,我们会选择蓝线的右端点 6 号点,接下来扫到第二条红线时,由于它上面并没有点被选取,所以又会选取它的右端点 5 号点,

HDU 2795 Billboard (线段树+贪心)

手动博客搬家:本文发表于20170822 21:30:17, 原地址https://blog.csdn.net/suncongbo/article/details/77488127 URL: http://acm.hdu.edu.cn/showproblem.php?pid=2795题目大意:有一个h*w的木板 (h, w<=1e9), 现在有n (n<=2e5)张1*xi的海报要贴在木板上,按1~n的顺序每次贴海报时会选择最上的一排的最左边贴 (海报不能互相覆盖), 求每张海报会被贴在哪一行

Codeforces 777D Hanoi Factory(线段树维护DP)

题目链接 Hanoi Factory 很容易想到这是一个DAG模型,那么状态转移方程就出来了. 但是排序的时候有个小细节:b相同时看a的值. 因为按照惯例,堆塔的时候肯定是内半径大的在下面. 因为N有1e5,那么DP的时候用线段树优化一下,就可以了. 1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 #define rep(i, a, b) for(int i(a); i <= (b); ++i) 6 7 typedef lo

CodeForces 91B Queue (线段树单点操作)

Description There are n walruses standing in a queue in an airport. They are numbered starting from the queue's tail: the 1-st walrus stands at the end of the queue and the n-th walrus stands at the beginning of the queue. The i-th walrus has the age