codeforces 1295E Permutation Separation 思維+線段樹

https://vjudge.net/problem/CodeForces-1295E
在這裏插入圖片描述
題目大意:p1,p2pnp_1,p_2……p_nnn的某個全排列,aia_i爲移動pip_i所需要的花費,你可以選取任意k,1<=k<nk,1<=k<n,把序列pp分成兩部分,即p1,,pkp_1,……,p_kpk+1,,pnp_{k+1},……,p_n。然後你可以做如下操作,即每次從任意一個序列中選取某個元素移動到另一個序列中,最終你需要使得第一個序列中的任意一個元素小於第二個序列中的任意一個元素(即前者中的最大值小於後者中的最小值),問最小花費是多少(如果這兩個序列中任意一個序列爲空,則認爲該方案也符合題意)。

思路:主要是轉換這一步比較難想。設值valval滿足第一個序列中的元素<val<val,第二個序列中的元素>=val>=val,由於ppnn的全排列,那麼valval的取值範圍爲[1,n+1][1,n+1](包含了某個序列爲空的情況)。假設valval爲一定值,t[pos]t[pos]表示選取k=posk=pos時所需的花費,那麼顯然t[pos]t[pos]由兩部分組成:
cost1=ai  1<=i<=pos && pi>=valcost_1=\sum a_i\ \ 其中1<=i<=pos\ \&\&\ p_i>=val
cost2=ai  pos<i<=n && pi<valcost_2=\sum a_i\ \ 其中pos<i<=n\ \&\&\ p_i<val
很明顯,當valval爲定值時,t[1n]t[1……n]可通過遞推計算出,複雜度爲O(n)O(n)。然而1<=val<=n+11<=val<=n+1,所以暴力的複雜度爲O(n2)O(n^2),顯然不行。那麼我們思考一下當valval增加11時,數組tt的變化情況,首先看下圖:
在這裏插入圖片描述
不難發現,當valval增加11時,第一部分中值爲valval的元素不用移到第二部分了,且第二部分中值爲valval的元素需要移到第一部分,不妨設p[i]=valp[i]=val,那麼也就是說t[in]t[i……n]的花費可以減少a[t]a[t]t[1i1]t[1……i-1]的花費需要增加a[t]a[t],而我們所求的最優解=Min(t[1n1])=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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章