P5459 [BJOI2016]回转寿司 题解 Link Solve Code

P5459 [BJOI2016]回转寿司

Solve

这道题的题面描述可能有点问题,这道题题面上说是子序列,实际上选的是一个连续的子序列。

由于每次只能选连续的一段,我们就求一个前缀和(S[i]= sum_{j=1}^ia[i])

题目问的是连续一段的总代价在([L,R]),所以

(L≤ sum_{k=i}^{j} a[i]≤R) (Rightarrow L≤S[i]-S[j-1]≤R)

(Rightarrow S[i]-R≤S[j-1]≤S[i]-L,(j≤i))

题目就转化成,枚举到一个(S[i])求之前的,在([S[i]-R,S[i]-L])(S[j])的个数

用树状数组或者线段树就可以处理了

Code

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=100005;
const LL maxR=1e10;
int N,tot,root;
LL L,R,S[maxn],ans;
struct node{
	int lson,rson,sum;
}Tree[13600005];
inline int read(){
	int ret=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
	while(ch<='9'&&ch>='0')ret=ret*10+ch-'0',ch=getchar();
	return ret*f;
}

inline void push_up(int x){
	Tree[x].sum=Tree[Tree[x].lson].sum+Tree[Tree[x].rson].sum;
	return ;
}

inline int build(){
	++tot;Tree[tot].lson=Tree[tot].rson=Tree[tot].sum=0;
	return tot;
}

int update(int x,LL l,LL r,LL pos,int val){
	if(!x)x=build();
	if(l==r){Tree[x].sum+=val;return x;}
	LL mid=r+l>>1;
	if(pos<=mid)Tree[x].lson=update(Tree[x].lson,l,mid,pos,val);
	else Tree[x].rson=update(Tree[x].rson,mid+1,r,pos,val);
	push_up(x);
	return x;
}

int query(int x,LL l,LL r,LL ql,LL qr){
	if(!x)x=build();
	if(ql<=l&&r<=qr)return Tree[x].sum;
	LL ret=0,mid=l+r>>1;
	if(ql<=mid)  ret+=query(Tree[x].lson,l,mid,ql,qr);
	if(qr> mid)ret+=query(Tree[x].rson,mid+1,r,ql,qr);
	return ret;
}

int main(){
	freopen("P5459.in","r",stdin);
	freopen("P5459.out","w",stdout);
	N=read();L=read();R=read();
	for(int i=1;i<=N;i++)S[i]=S[i-1]+read();
	root=update(root,-maxR,maxR,S[0],1);
	for(int i=1;i<=N;i++){
		ans+=query(root,-maxR,maxR,S[i]-R,S[i]-L);
		update(root,-maxR,maxR,S[i],1);
	}
	printf("%lld
",ans);
	return 0;
}