bzoj 5319: [Jsoi2018]军训列队
Description
Solution
最优情况可以是所有人按位置从小到大排序之后依次占到自己 \(K+\) 排名的位置上去
因为每一个休息位置不同,那么一定递增,所以一定存在一个分界点,左边的是往右走,右边的往左走
在主席树上二分出这个分界点即可
#include<bits/stdc++.h>
using namespace std;
template<class T>void gi(T &x){
int f;char c;
for(f=1,c=getchar();c<'0'||c>'9';c=getchar())if(c=='-')f=-1;
for(x=0;c<='9'&&c>='0';c=getchar())x=x*10+(c&15);x*=f;
}
typedef long long ll;
const int N=500010,inf=1e9;
int n,Q,a[N],rt[N],tt=0,m=0,K,c=0;ll sum[N];
struct data{
int ls,rs,w;ll s;
}tr[N*25];
inline void ins(int &x,int l,int r,int sa){
tr[++tt]=tr[x];x=tt;tr[x].w++;tr[x].s+=sa;
if(l==r)return ;
int mid=(l+r)>>1;
if(sa<=mid)ins(tr[x].ls,l,mid,sa);
else ins(tr[x].rs,mid+1,r,sa);
}
inline ll qry(int x,int y,int l,int r){
if(l==r){
if(tr[x].w-tr[y].w && l<=K){c++;return l;}
return 0;
}
int mid=(l+r)>>1,o=tr[tr[x].ls].w-tr[tr[y].ls].w;
if(!o || mid<=o+K-1){
c+=o;K+=o;
return qry(tr[x].rs,tr[y].rs,mid+1,r)+tr[tr[x].ls].s-tr[tr[y].ls].s;
}
return qry(tr[x].ls,tr[y].ls,l,mid);
}
int main(){
freopen("pp.in","r",stdin);
freopen("pp.out","w",stdout);
cin>>n>>Q;
for(int i=1;i<=n;i++)gi(a[i]),m=max(m,a[i]),sum[i]=sum[i-1]+a[i];
for(int i=1;i<=n;i++)rt[i]=rt[i-1],ins(rt[i],1,m,a[i]);
int l,r,k;
while(Q--){
gi(l);gi(r);gi(k);K=k;c=0;
ll tot=qry(rt[r],rt[l-1],1,m);
ll ans=(1ll*c*(2*k+c-1)>>1)-tot+
sum[r]-sum[l-1]-tot-(1ll*(c+2*k+r-l)*(r-l+1-c)>>1);
printf("%lld\n",ans);
}
return 0;
}