codeforces 1295E Permutation Separation 思维+线段树

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

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

#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;
}

发布了677 篇原创文章 · 获赞 30 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/xiji333/article/details/104213448