初涉值域线段树
其实就是线段树啦
什么是值域线段树
我们(初学者)都知道线段树是拿来维护数列区间的信息的。但是如果我们想要查询的重点在于数值而不是区间信息呢?这时候就要对于值域区间维护线段树了。
例题
bzoj4627: [BeiJing2016]回转寿司
题目大意
给定$n$个数和$L,R$,问其中区间和满足$L<=sum[r]-sum[l-1]<=R$的区间有多少
N≤100000,|Ai|≤100000,0≤L, R≤10^9
题目分析
值域线段树中每个节点代表一个值的区间,其实和基础的线段树差不多。
还有这题由于LR很大,并且没办法离散化,所以还要动态开点线段树。
hint:注意一下数组的范围。
1 #include<bits/stdc++.h> 2 typedef long long ll; 3 const ll INF = 1e10; 4 const int maxn = 6000035; 5 6 struct node 7 { 8 int d[2]; 9 ll val; 10 int &operator [](int a) 11 { 12 return d[a]; 13 } 14 }a[maxn]; 15 int n,l,r,root,tot; 16 ll s[maxn],ans; 17 18 int read() 19 { 20 char ch = getchar(); 21 int num = 0; 22 bool fl = 0; 23 for (; !isdigit(ch); ch = getchar()) 24 if (ch=='-') fl = 1; 25 for (; isdigit(ch); ch = getchar()) 26 num = (num<<1)+(num<<3)+ch-48; 27 if (fl) num = -num; 28 return num; 29 } 30 void update(int &x, ll L, ll R, ll c) 31 { 32 if (!x){ 33 x = ++tot; 34 a[x][0] = a[x][1] = a[x].val = 0; 35 } 36 a[x].val++; 37 if (L==R) return; 38 ll mid = (L+R)>>1; 39 if (mid >= c) 40 update(a[x][0], L, mid, c); 41 else update(a[x][1], mid+1, R, c); 42 } 43 ll query(int x, ll L, ll R, ll l, ll r) 44 { 45 if (L <= l&&r <= R) return a[x].val; 46 ll mid = (l+r)>>1, ret = 0; 47 if (L <= mid && a[x][0]) ret += query(a[x][0], L, R, l, mid); 48 if (R > mid && a[x][1]) ret += query(a[x][1], L, R, mid+1, r); 49 return ret; 50 } 51 int main() 52 { 53 n = read(), l = read(), r = read(); 54 for (int i=1; i<=n; i++) 55 s[i] = s[i-1]+read(); 56 update(root, -INF, INF, 0); 57 for (int i=1; i<=n; i++) 58 { 59 ans += query(root, s[i]-r, s[i]-l, -INF, INF); 60 update(root, -INF, INF, s[i]); 61 } 62 printf("%lld\n",ans); 63 return 0; 64 }
END