BZOJ5319: [Jsoi2018]军训列队

BZOJ5319: [Jsoi2018]军训列队

https://lydsy.com/JudgeOnline/problem.php?id=5319

分析:

  • 易知把所有人按原本的顺序放到\([K,K+len-1]\)这些位置上是最优的。
  • 我们只需要求一个\(mid\), 满足从\(mid\)以后的人都是向左移动。
  • 这个可以用二分+主席树在\(O(nlog^2)\)的时间内解决, 然后过不去。
  • 变成直接在主席树上二分就好了,需要一点小技巧。
  • 即二分走的区间不一定包含答案点,但可能是答案点减\(1​\), 在叶子上特判即可。

代码:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
typedef long long ll;
#define N 500050
#define M 1500000
int n,m,a[N];
int siz[N*30],ls[N*30],rs[N*30],cnt,root[N];
ll sum[N*30];
void update(int l,int r,int x,int &p,int q) {
    p=++cnt; ls[p]=ls[q]; rs[p]=rs[q]; siz[p]=siz[q]+1; sum[p]=sum[q]+x;
    if(l==r) return ;
    int mid=(l+r)>>1;
    if(x<=mid) update(l,mid,x,ls[p],ls[q]);
    else update(mid+1,r,x,rs[p],rs[q]);
}
ll c,now,s[N];
ll S(ll l,ll r) {return (l+r)*(r-l+1)/2;}
ll query(int l,int r,int p,int q) {
    if(l==r) {
        if(siz[p]>siz[q]&&l<now) {c++; return l;}
        else return 0;
    }
    int mid=(l+r)>>1,sizls=siz[ls[p]]-siz[ls[q]];
    if(!sizls) return query(mid+1,r,rs[p],rs[q]);
    if(mid<=now+sizls-1) {
        c+=sizls, now+=sizls;
        return query(mid+1,r,rs[p],rs[q])+sum[ls[p]]-sum[ls[q]];
    }else return query(l,mid,ls[p],ls[q]);
}
int main() {
    scanf("%d%d",&n,&m);
    int i,l,r,k;
    for(i=1;i<=n;i++) scanf("%d",&a[i]),s[i]=s[i-1]+a[i],update(1,M,a[i],root[i],root[i-1]);
    while(m--) {
        scanf("%d%d%d",&l,&r,&k);
        now=k; c=0;
        ll tot=query(1,M,root[r],root[l-1]);
        
        ll ans=S(k,k+c-1)-tot  +  s[r]-s[l-1]-tot-S(k+c,k+r-l);
        printf("%lld\n",ans);
    }
}

转载于:https://www.cnblogs.com/suika/p/10264016.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值