这是题目链接4627: [BeiJing2016]回转寿司
题目大意:
给定n个数,求有多少个字段和在 满足 L<=sum<=R;
解题思路
需要解这个题目,需要有线段树加可持续化的思想,但是这个题目只需要上一棵线段树的信息,所以可以不用主席树,只要用到值域线段树。那么,这样就可以把问题转化为:
先处理处前缀和。 求 L<=sum[j]-sum[i]<=R (0 <=i < j<=n) 有多少个?
那么我们可以用值域线段树搞一下,线段树节点存值的个数,每次把前缀和这个值插入,每次查询,原问题就等同于 当前线段树中有多少个值是在 [ sum[j]-R , sum[j]-L ] 这个区间,
意思就是,每次把上一个前缀和插入需要计算一下上一棵线段树值在[ sum[j]-R , sum[j]-L ] 个数。
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <vector> 5 #include <iostream> 6 using namespace std; 7 8 typedef long long int LL; 9 const LL INF=10000000000ll; 10 const int maxn=100000*32; 11 struct ACM 12 { 13 struct Node 14 { 15 LL val; 16 int lson,rson; 17 }seg[maxn]; 18 int sz; 19 int newnode() 20 { 21 sz++; 22 seg[sz].val=0; 23 seg[sz].rson=-1; 24 seg[sz].lson=-1; 25 return sz; 26 } 27 void init() 28 { 29 sz=-1; 30 } 31 void update(int &root,LL l,LL r,LL val) 32 { 33 if(root==-1) root=newnode(); 34 seg[root].val++; 35 if(l==r) return ; 36 LL mid=(l+r)>>1; 37 if(val<=mid) update(seg[root].lson,l,mid,val); 38 else update(seg[root].rson,mid+1,r,val); 39 } 40 LL query(int root,LL l,LL r,LL ql,LL qr) 41 { 42 if(root==-1) return 0; 43 if(l==ql&&r==qr) return seg[root].val; 44 LL mid=(l+r)>>1; 45 if(qr<=mid) return query(seg[root].lson,l,mid,ql,qr); 46 else if(ql>mid) return query(seg[root].rson,mid+1,r,ql,qr); 47 else return query(seg[root].lson,l,mid,ql,mid)+query(seg[root].rson,mid+1,r,mid+1,qr); 48 } 49 /** 50 void debug(int i) 51 { 52 if(i==-1) return ; 53 printf("node=%d val=%lld lson=%d rson=%d\n",i,seg[i].val,seg[i].lson,seg[i].rson); 54 debug(seg[i].lson),debug(seg[i].rson); 55 } 56 */ 57 }AC; 58 59 int main() 60 { 61 int n,L,R,root=-1; 62 scanf("%d%d%d",&n,&L,&R); 63 LL sum=0,ans=0; 64 AC.init(); 65 for(int i=1; i<=n; i++) 66 { 67 int num; 68 scanf("%d",&num); 69 AC.update(root,-INF,INF,sum); 70 sum+=num; 71 ans+=AC.query(root,-INF,INF,sum-R,sum-L); 72 } 73 printf("%lld\n",ans); 74 return 0; 75 }