luogu P5459 [BJOI2016]回转寿司

题面传送门
题目要求有多少个 i , j i,j ij使的 l ≤ ∑ k = i j a i ≤ r l\leq \sum\limits_{k=i}^{j}{a_i}\leq r lk=ijair
用前缀和搞一下,变成 l ≤ q j − q i − 1 ≤ r l\leq q_j-q_{i-1}\leq r lqjqi1r
再变换一下,变成 q j − r ≤ q i − 1 ≤ q j − l q_j-r\leq q_{i-1}\leq q_j-l qjrqi1qjl
然后动态开点线段树或者平衡树一搞就好了。
代码实现:

#include
using namespace std;
long long n,m,k,x,y,z,a[100039],tot=1;
long long q[100039],ans,l,r;
struct tree{
     int l,r,sum;}f[5000039];
inline void get(long long x,long long l,long long r,int now){
     
	if(l==r){
     f[now].sum++;return;}
	long long m=(l+r)>>1;
	if(x<=m){
     
		if(!f[now].l) f[now].l=++tot;
		get(x,l,m,f[now].l);
	}
	else{
     
		if(!f[now].r) f[now].r=++tot;
		get(x,m+1,r,f[now].r);
	}
	f[now].sum=f[f[now].l].sum+f[f[now].r].sum;
}
inline int find(long long x,long long y,long long l,long long r,int now){
     
	if(x<=l&&r<=y) return f[now].sum;
	long long m=(l+r)>>1;
	int fs=0;
	if(x<=m&&f[now].l) fs+=find(x,y,l,m,f[now].l);
	if(y>m&&f[now].r) fs+=find(x,y,m+1,r,f[now].r);
	return fs;
}
int main(){
     
	//freopen("1.in","r",stdin);
	register int i;
	scanf("%lld%lld%lld",&n,&l,&r);
	for(i=1;i<=n;i++) scanf("%lld",&a[i]),q[i]=q[i-1]+a[i];
	get(0,(long long)-1e10-1e9,(long long)1e10,1);
	for(i=1;i<=n;i++){
     
		ans+=find(q[i]-r,q[i]-l,(long long)-1e10-1e9,(long long)1e10,1);
		get(q[i],(long long)-1e10-1e9,(long long)1e10,1);
	}
	printf("%lld\n",ans);
}

你可能感兴趣的:(洛谷,前缀和,线段树,动态开点,平衡树)