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

posted @ 2018-07-15 11:55  PIPIBoss  阅读(256)  评论(0编辑  收藏  举报