题意:
求在一个序列中和在区间[l,r]中的连续子序列的个数。序列大小≤100000,序列元素可以为负数。
题解:
题目要求这个:l<=sum[i]-sum[j-1]<=r,移项得sum[i]-l>=sum[j-1]>=sum[i]-r,故题目转化为求一个集合中值在某个区间的元素个数。神犇们的题解里用的都是权值线段树,然而我不知道怎么离散化,所以写了一个treap,不过由于写得不熟,又WA了好几发。注意检查有没有需要用long long的地方却没用。
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <set> 5 #define inc(i,j,k) for(int i=j;i<=k;i++) 6 #define ll long long 7 #define maxn 100010 8 using namespace std; 9 10 inline int read(){ 11 char ch=getchar(); int f=1,x=0; 12 while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();} 13 while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar(); 14 return f*x; 15 } 16 ll v[maxn]; int ch[maxn][2],sz[maxn],cnt[maxn],tot,root,rnd[maxn]; 17 void update(int x){sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+cnt[x];} 18 void rotate(int &x,bool c){ 19 int y=ch[x][c]; ch[x][c]=ch[y][!c]; ch[y][!c]=x; update(x); update(y); x=y; 20 } 21 void insert(int &x,ll val){ 22 if(!x){x=++tot; sz[x]=cnt[x]=1; v[x]=val; rnd[x]=rand(); return;} 23 if(val==v[x]){cnt[x]++; sz[x]++; return;} 24 if(val<v[x]){insert(ch[x][0],val); update(x); if(rnd[ch[x][0]]<rnd[x])rotate(x,0); return;} 25 if(val>v[x]){insert(ch[x][1],val); update(x); if(rnd[ch[x][1]]<rnd[x])rotate(x,1); return;} 26 } 27 int query1(int x,ll val){ 28 if(!x)return 0; 29 if(val<v[x])return sz[ch[x][1]]+cnt[x]+query1(ch[x][0],val); 30 if(val==v[x])return sz[ch[x][1]]+cnt[x]; 31 if(val>v[x])return query1(ch[x][1],val); 32 } 33 int query2(int x,ll val){ 34 if(!x)return 0; 35 if(val<v[x])return sz[ch[x][1]]+cnt[x]+query2(ch[x][0],val); 36 if(val==v[x])return sz[ch[x][1]]; 37 if(val>v[x])return query2(ch[x][1],val); 38 } 39 int n; ll sm,ans,l,r; 40 int main(){ 41 n=read(); l=read(); r=read(); insert(root,0); 42 inc(i,1,n){ 43 ll a=read(); sm+=a; int x=query1(root,sm-r),y=query2(root,sm-l); ans+=(ll)x-y; insert(root,sm); 44 } 45 printf("%lld",ans); return 0; 46 }
20160818