动态开点线段树
普通线段树使用数组下标来表示左右儿子,即完全二叉树的方法。
动态开点线段树:与普通线段树相比,用指针表示左右儿子(类似普通二叉树)。
用于数据范围大但是节点利用率低的情况。
从效果上看有点像离散化。
题目:P5459
该题使用线段树时,会发现根节点范围包括了整个值域,但是实际上只有n个叶子节点有记录值。因此使用动态开点来避免大量无用空间。
定义:(由于仅有单点修改,无需懒标记)
struct node{
int lc, rc;
ll data;
}t[MAXN << 8];
单点修改:
void pushup(int p){
t[p].data = t[t[p].lc].data + t[t[p].rc].data;
}
void insert(int p, ll l, ll r, ll num){
if(l == r){
t[p].data++;
return ;
}
ll mid = (l + r) >> 1;
if(num <= mid){
if(t[p].lc == 0){
tot++;
t[p].lc = tot;
}
insert(t[p].lc, l, mid, num);
}
else{
if(t[p].rc == 0){
tot++;
t[p].rc = tot;
}
insert(t[p].rc, mid + 1, r, num);
}
pushup(p);
}
区间求和:
ll qsum(int p, ll l, ll r, ll x, ll y){
if(p == 0) return 0;
if(x <= l && r <= y){
return t[p].data;
}
ll res = 0;
ll mid = (l + r) >> 1;
if(mid >= x) res += qsum(t[p].lc, l, mid, x, y);
if(y >= mid + 1) res += qsum(t[p].rc, mid + 1, r, x, y);
return res;
}
完整代码:
#include <bits/stdc++.h>
#define MAXN 100005
#define MAXM 10000000000
#define ll long long
using namespace std;
struct node{
int lc, rc;
ll data;
}t[MAXN << 8];
int tot;
int n, L, R;
ll pre[MAXN];
void pushup(int p){
t[p].data = t[t[p].lc].data + t[t[p].rc].data;
}
void insert(int p, ll l, ll r, ll num){
if(l == r){
t[p].data++;
return ;
}
ll mid = (l + r) >> 1;
if(num <= mid){
if(t[p].lc == 0){
tot++;
t[p].lc = tot;
}
insert(t[p].lc, l, mid, num);
}
else{
if(t[p].rc == 0){
tot++;
t[p].rc = tot;
}
insert(t[p].rc, mid + 1, r, num);
}
pushup(p);
}
ll qsum(int p, ll l, ll r, ll x, ll y){
if(p == 0) return 0;
if(x <= l && r <= y){
return t[p].data;
}
ll res = 0;
ll mid = (l + r) >> 1;
if(mid >= x) res += qsum(t[p].lc, l, mid, x, y);
if(y >= mid + 1) res += qsum(t[p].rc, mid + 1, r, x, y);
return res;
}
int main(){
scanf("%d%d%d", &n, &L, &R);
ll a = 0;
for(int i = 1; i <= n; i++){
scanf("%lld", &a);
pre[i] = pre[i - 1] + a;
}
ll ans = 0;
insert(1, -MAXM, MAXM, 0);
for(int i = 1; i <= n; i++){
ans += qsum(1, -MAXM, MAXM, pre[i] - R, pre[i] - L);
insert(1, -MAXM, MAXM, pre[i]);
}
printf("%lld", ans);
return 0;
}

浙公网安备 33010602011771号