Educational Codeforces Round 81 (Rated for Div. 2) E. Permutation Separation(前缀和,线段树)

链接:Educational Codeforces Round 81 (Rated for Div. 2) E. Permutation Separation

题意

给出一个 1 1 1 ~ n n n 2 ≤ n ≤ 2 ⋅ 1 0 5 2\le n\le 2\cdot 10^5 2n2105)的排列 p 1 , p 2 , ⋯   , p n p_1,p_2,\cdots,p_n p1,p2,,pn,选择 k k k并将序列分为左右两个集合 p 1 , p 2 , ⋯   , p k p_1,p_2,\cdots,p_k p1,p2,,pk p k + 1 , p k + 2 , ⋯   , p n p_{k+1},p_{k+2},\cdots,p_n pk+1,pk+2,,pn 1 ≤ k ≤ n − 1 1\le k\le n-1 1kn1),移动元素 p i p_i pi至另一个集合的代价为 a i a_i ai k k k任选,求 使得最终左集合中所有元素小于右集合中所有元素的最小总代价。(任意集合为空时也符合要求)


分析

首先考虑使得 最终某一集合为空 时的最小总代价,显然是当 k = 1 k=1 k=1 k = n − 1 k=n-1 k=n1时,故取 m i n { a [ 1 ] , a [ n ] } min\{a[1],a[n]\} min{a[1],a[n]}

接下来考虑 最终左集合中含有元素为 1 , 2 , ⋯   , i 1,2,\cdots,i 1,2,,i 1 ≤ i ≤ n − 1 1\le i\le n-1 1in1),则我们可以枚举 i i i,然后每次取找到最优的划分 k k k(即找到最小的代价)朴素解法下这样的时间复杂度会达到 O ( n 2 ) O(n^2) O(n2),故需要对寻找最优划分 k k k的过程进行优化;


观察当 i = 1 i=1 i=1时,设 i i i在原排列中的位置为 p o s [ i ] pos[i] pos[i]

  1. k < p o s [ 1 ] k\lt pos[1] k<pos[1]时( 1 1 1被划分至右集合),代价为 ∑ j = 1 k a [ j ] + a [ p o s [ 1 ] ] \sum\limits_{j=1}^k a[j]+a[pos[1]] j=1ka[j]+a[pos[1]](左集合元素均移至右集合, 1 1 1移至左集合);
  2. 而当 k ≥ p o s [ 1 ] k\ge pos[1] kpos[1]时( 1 1 1被划分至左集合),代价为 ∑ j = 1 k a [ j ] − a [ p o s [ 1 ] ] \sum\limits_{j=1}^{k}a[j]-a[pos[1]] j=1ka[j]a[pos[1]](左集合元素除 1 1 1以外均移至右集合);

这样可以看出,代价是和前缀和有关的,可以设置一个总代价数组 t [ 1 ] , t [ 2 ] , ⋯   , t [ n − 1 ] t[1],t[2],\cdots,t[n-1] t[1],t[2],,t[n1],初始状态下 t [ k ] = ∑ j = 1 k a [ j ] t[k]=\sum\limits_{j=1}^{k}a[j] t[k]=j=1ka[j],即前缀和,当枚举至 i i i时,令 t [ 1 ⋯ p o s [ i ] − 1 ] + a [ p o s [ i ] ] t[1\cdots pos[i]-1]+a[pos[i]] t[1pos[i]1]+a[pos[i]],令 t [ p o s [ i ] ⋯ n − 1 ] − a [ p o s [ i ] ] t[pos[i]\cdots n-1]-a[pos[i]] t[pos[i]n1]a[pos[i]],利用 min ⁡ 1 ≤ k ≤ n − 1 { t [ k ] } \min\limits_{1\le k\le n-1}\{t[k]\} 1kn1min{t[k]}更新答案即可。

线段树维护区间最值,最终时间复杂度为 O ( n log ⁡ n ) O(n\log n) O(nlogn)



分析

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF=0x3f3f3f3f;
const int LINF=0x3f3f3f3f3f3f3f3f;
const int MOD=1e9+7;
const int maxn=2e5+10;
int n,p[maxn],a[maxn],pos[maxn];
LL sum[maxn],t[maxn<<2],laz[maxn<<2];
void push_down(int rt)
{
    if(laz[rt])
    {
        laz[rt<<1]+=laz[rt];
        laz[rt<<1|1]+=laz[rt];
        t[rt<<1]+=laz[rt];
        t[rt<<1|1]+=laz[rt];
        laz[rt]=0;
    }
}
void build(int rt,int l,int r)
{
    if(l==r)
    {
        t[rt]=sum[l];
        return;
    }
    int mid=(l+r)>>1;
    build(rt<<1,l,mid);
    build(rt<<1|1,mid+1,r);
    t[rt]=min(t[rt<<1],t[rt<<1|1]);
}
void updata(int rt,int l,int r,int ql,int qr,LL val)
{
    if(ql<=l&&r<=qr)
    {
        laz[rt]+=val;
        t[rt]+=val;
        return;
    }
    push_down(rt);
    int mid=(l+r)>>1;
    if(ql<=mid)
        updata(rt<<1,l,mid,ql,qr,val);
    if(qr>mid)
        updata(rt<<1|1,mid+1,r,ql,qr,val);
    t[rt]=min(t[rt<<1],t[rt<<1|1]);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&p[i]);
        pos[p[i]]=i;
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        sum[i]=sum[i-1]+a[i];
    }
    build(1,1,n-1);
    LL ans=min(a[1],a[n]);
    for(int i=1;i<=n-1;i++)
    {
        if(pos[i]>1)
            updata(1,1,n-1,1,pos[i]-1,a[pos[i]]);
        if(pos[i]<n)
            updata(1,1,n-1,pos[i],n-1,-a[pos[i]]);
        ans=min(ans,t[1]);
    }
    printf("%lld\n",ans);
    return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值