codeforces 1295E Permutation Separation 思维+线段树

42 篇文章 0 订阅
33 篇文章 0 订阅

https://vjudge.net/problem/CodeForces-1295E
在这里插入图片描述
题目大意: p 1 , p 2 … … p n p_1,p_2……p_n p1,p2pn n n n的某个全排列, a i a_i ai为移动 p i p_i pi所需要的花费,你可以选取任意 k , 1 < = k < n k,1<=k<n k,1<=k<n,把序列 p p p分成两部分,即 p 1 , … … , p k p_1,……,p_k p1,,pk p k + 1 , … … , p n p_{k+1},……,p_n pk+1,,pn。然后你可以做如下操作,即每次从任意一个序列中选取某个元素移动到另一个序列中,最终你需要使得第一个序列中的任意一个元素小于第二个序列中的任意一个元素(即前者中的最大值小于后者中的最小值),问最小花费是多少(如果这两个序列中任意一个序列为空,则认为该方案也符合题意)。

思路:主要是转换这一步比较难想。设值 v a l val val满足第一个序列中的元素 < v a l <val <val,第二个序列中的元素 > = v a l >=val >=val,由于 p p p n n n的全排列,那么 v a l val val的取值范围为 [ 1 , n + 1 ] [1,n+1] [1,n+1](包含了某个序列为空的情况)。假设 v a l val val为一定值, t [ p o s ] t[pos] t[pos]表示选取 k = p o s k=pos k=pos时所需的花费,那么显然 t [ p o s ] t[pos] t[pos]由两部分组成:
c o s t 1 = ∑ a i    其 中 1 < = i < = p o s   & &   p i > = v a l cost_1=\sum a_i\ \ 其中1<=i<=pos\ \&\&\ p_i>=val cost1=ai  1<=i<=pos && pi>=val
c o s t 2 = ∑ a i    其 中 p o s < i < = n   & &   p i < v a l cost_2=\sum a_i\ \ 其中pos<i<=n\ \&\&\ p_i<val cost2=ai  pos<i<=n && pi<val
很明显,当 v a l val val为定值时, t [ 1 … … n ] t[1……n] t[1n]可通过递推计算出,复杂度为 O ( n ) O(n) O(n)。然而 1 < = v a l < = n + 1 1<=val<=n+1 1<=val<=n+1,所以暴力的复杂度为 O ( n 2 ) O(n^2) O(n2),显然不行。那么我们思考一下当 v a l val val增加 1 1 1时,数组 t t t的变化情况,首先看下图:
在这里插入图片描述
不难发现,当 v a l val val增加 1 1 1时,第一部分中值为 v a l val val的元素不用移到第二部分了,且第二部分中值为 v a l val val的元素需要移到第一部分,不妨设 p [ i ] = v a l p[i]=val p[i]=val,那么也就是说 t [ i … … n ] t[i……n] t[in]的花费可以减少 a [ t ] a[t] a[t] t [ 1 … … i − 1 ] t[1……i-1] t[1i1]的花费需要增加 a [ t ] a[t] a[t],而我们所求的最优解 = M i n ( t [ 1 … … n − 1 ] ) =Min(t[1……n-1]) =Min(t[1n1]),区间修改、查询最小值……不就是线段树模板题吗!

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;

const int maxn=2e5+5;

struct Tree
{
    int l,r;
    ll lazy,MIN;
}tree[maxn<<2];

int n;
int a[maxn],p[maxn],idx[maxn];
ll t[maxn];

inline void up(int i)
{
    tree[i].MIN=min(tree[i<<1].MIN,tree[i<<1|1].MIN);
}

inline void down(int i)
{
    int l=i<<1,r=i<<1|1;
    tree[l].lazy+=tree[i].lazy,tree[r].lazy+=tree[i].lazy;
    tree[l].MIN+=tree[i].lazy,tree[r].MIN+=tree[i].lazy;
    tree[i].lazy=0;
}

void build(int i,int l,int r)
{
    tree[i].l=l,tree[i].r=r,tree[i].lazy=0;
    if(l==r)
    {
        tree[i].MIN=t[l];
        return ;
    }
    int mid=l+r>>1;
    build(i<<1,l,mid);
    build(i<<1|1,mid+1,r);
    up(i);
}

void update(int i,int l,int r,int v)
{
    if(tree[i].l==l&&tree[i].r==r)
    {
        tree[i].MIN+=v,tree[i].lazy+=v;
        return ;
    }
    if(tree[i].lazy)
        down(i);
    int mid=tree[i].l+tree[i].r>>1;
    if(mid>=r)
        update(i<<1,l,r,v);
    else if(mid<l)
        update(i<<1|1,l,r,v);
    else
        update(i<<1,l,mid,v),update(i<<1|1,mid+1,r,v);
    up(i);
}

ll query(int i,int l,int r)
{
    if(tree[i].l==l&&tree[i].r==r)
        return tree[i].MIN;
    if(tree[i].lazy)
        down(i);
    int mid=tree[i].l+tree[i].r>>1;
    if(mid>=r)
        return query(i<<1,l,r);
    else if(mid<l)
        return query(i<<1|1,l,r);
    else
        return min(query(i<<1,l,mid),query(i<<1|1,mid+1,r));
    up(i);
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&p[i]),idx[p[i]]=i;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        t[i]=t[i-1]+a[i];
    }
    build(1,1,n);
    int val=1,id;
    ll ans=query(1,1,n-1);
    while(val<=n)
    {
        id=idx[val];
        update(1,id,n,-a[id]);
        if(id>1)
            update(1,1,id-1,a[id]);
        ans=min(ans,query(1,1,n-1));
        ++val;
    }
    printf("%lld\n",ans);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值