2019.03.08【JSOI2018】【洛谷P4559】【BZOJ5319】军训列队(主席树)

版权声明:转载请声明出处,谢谢配合。 https://blog.csdn.net/zxyoi_dreamer/article/details/88359774

洛谷传送门

BZOJ传送门


解析:

JSOI2018最简单的一道题。。。

显然有一种最优解就是保持所有同学相对位置不变。

显然所有同学向左和向右跑的决策是有一个分界线的。

直接在主席树上二分就行了。


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<20|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	inline int getint(){
		re char c;
		while(!isdigit(c=gc()));re int num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
}
using namespace IO;

cs int N=1e6,M=(N+6)*20;
int rt[500005],son[M][2],cnt[M],tot;
ll sum[M];

void insert(int pre,int &now,int l,int r,int pos){
	now=++tot;
	cnt[now]=cnt[pre]+1;
	sum[now]=sum[pre]+pos;
	son[now][0]=son[pre][0];
	son[now][1]=son[pre][1];
	if(l==r)return ;
	int mid=(l+r)>>1;
	if(pos<=mid)insert(son[pre][0],son[now][0],l,mid,pos);
	else insert(son[pre][1],son[now][1],mid+1,r,pos);
}

ll query(int tl,int tr,int l,int r,cs int &l1,cs int &r1){
	if(r1<l1)return 0;
	if(l1<=l)return sum[tr]-sum[tl]-(ll)l1*(r1-l1+1)-(ll)(r1-l1)*(r1-l1+1)/2;
	if(r<=r1)return (ll)r1*(r1-l1+1)-sum[tr]+sum[tl]-(ll)(r1-l1)*(r1-l1+1)/2;
	int mid=(l+r)>>1;
	int delta=cnt[son[tr][0]]-cnt[son[tl][0]];
	return query(son[tl][0],son[tr][0],l,mid,l1,l1+delta-1)+query(son[tl][1],son[tr][1],mid+1,r,l1+delta,r1);
}

int n,m;
signed main(){
	n=getint(),m=getint();
	for(int re i=1;i<=n;++i)insert(rt[i-1],rt[i],1,N,getint());
	int l,r,K;
	while(m--){
		l=getint(),r=getint(),K=getint();
		cout<<query(rt[l-1],rt[r],1,N,K,K+r-l)<<"\n";
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/zxyoi_dreamer/article/details/88359774