OI模板 线段树
线段树(区间改、区间查)with lazy tag
//P3372
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
struct SegmentTree{
struct Node{ int l, r; long long sum, lazy; } T[N*4];
int a[N], n;
inline void update(int p){
T[p].sum = T[p<<1].sum + T[p<<1|1].sum;
}
inline void spread(int p){
if(T[p].lazy){
T[p<<1].sum += T[p].lazy * (T[p<<1].r - T[p<<1].l + 1);
T[p<<1|1].sum += T[p].lazy * (T[p<<1|1].r - T[p<<1|1].l + 1);
T[p<<1].lazy += T[p].lazy;
T[p<<1|1].lazy += T[p].lazy;
T[p].lazy = 0;
}
}
inline void buildtree(int p, int l, int r){
T[p].l = l, T[p].r = r;
if(l == r){ T[p].sum = a[l]; return; }
int mid = l + r >> 1;
buildtree(p << 1, l, mid);
buildtree(p << 1 | 1, mid + 1, r);
update(p);
}
inline void modify(int p, int x, long long v){
if(T[p].l == T[p].r){ T[p].sum = v; return; }
spread(p);
int mid = T[p].l + T[p].r >> 1;
if(x <= mid) modify(T[p].l, x, v);
else modify(p << 1 | 1, x, v);
update(p);
}
inline void change(int p, int l, int r, long long v){
if(l <= T[p].l && T[p].r <= r){
T[p].sum += v * (T[p].r - T[p].l + 1);
T[p].lazy += v;
return;
}
spread(p);
int mid = T[p].l + T[p].r >> 1;
if(l <= mid) change(p << 1, l, r, v);
if(mid < r) change(p << 1 | 1, l, r, v);
update(p);
}
inline long long ask(int p, int l, int r){
if(l <= T[p].l && T[p].r <= r) return T[p].sum;
spread(p);
int mid = T[p].l + T[p].r >> 1;
long long val = 0;
if(l <= mid) val += ask(p << 1, l, r);
if(mid < r) val += ask(p << 1 | 1, l, r);
return val;
}
} T;
query 函数另一种写法:不用新变量的写法。
inline long long ask(int p, int l, int r){
if(l <= T[p].l && T[p].r <= r) return T[p].sum;
spread(p);
int mid = T[p].l + T[p].r >> 1;
if(l <= mid && mid < r) return ask(p << 1, l, r) + ask(p << 1 | 1, l, r);
else if(l <= mid) return ask(p << 1, l, r);
else if(mid < r) return ask(p << 1 | 1, l, r);
return 0;
}
线段树基础模型
1.最大子段和
维护 a l l _ m a x , l e f t _ m a x , r i g h t _ m a x , s u m all\_max,left\_max,right\_max,sum all_max,left_max,right_max,sum。
inline void update(int n){
t[n].sum=t[n<<1].sum+t[n<<1|1].sum;
t[n].lm=max(t[n<<1].lm,t[n<<1].sum+t[n<<1|1].lm);
t[n].rm=max(t[n<<1|1].rm,t[n<<1].rm+t[n<<1|1].sum);
t[n].am=max(max(t[n<<1].am,t[n<<1|1].am),t[n<<1].rm+t[n<<1|1].lm);
return;
}
query
函数中可以返回一个节点而不是一个值。
有时候会套二分,例子:[SHOI2015]脑洞治疗仪。
2.扫描线
-
取出 n n n 个矩形的左右边界,左边界记为 ( x 1 , y 1 , y 2 , 1 ) (x_1,y_1,y_2,1) (x1,y1,y2,1),右边界记为 ( x 2 , y 1 , y 2 , − 1 ) (x_2,y_1,y_2,-1) (x2,y1,y2,−1)(不妨设 x 1 < x 2 , y 1 < y 2 x_1<x_2,y_1<y_2 x1<x2,y1<y2),把这 2 n 2n 2n 个四元组按照 x x x 递增排序。
-
对 y y y 进行离散化,设 v a l ( y ) val(y) val(y) 表示 y y y 离散化后的值, r a w ( i ) raw(i) raw(i) 表示 i i i 对应的 y y y。设离散化后对应的 y y y 坐标值有 m m m 个。
-
扫描线最多分成 m − 1 m-1 m−1 段,其中第 i i i 段为区间 [ r a w ( i ) , r a w ( i + 1 ) ] [raw(i),raw(i+1)] [raw(i),raw(i+1)]。使用数组 c [ i ] c[i] c[i] 记录扫描线上第 i i i 段被覆盖的次数。
-
扫描 2 n 2n 2n 个四元组,设当前四元组为 ( x , y 1 , y 2 , k ) (x,y_1,y_2,k) (x,y1,y2,k),将 c [ v a l ( y 1 ) ] , c [ v a l ( y 1 ) + 1 ] , . . . , c [ v a l ( y 2 ) − 1 ] c[val(y_1)],c[val(y_1)+1],...,c[val(y_2)-1] c[val(y1)],c[val(y1)+1],...,c[val(y2)−1] 这些值都加上 k k k。
-
此时,如果下一个四元组的横坐标为 x 2 x_2 x2,则扫描线从 x x x 扫到 x 2 x_2 x2 时,被覆盖的长度为 ∑ c [ i ] > 0 ( r a w ( i + 1 ) − r a w ( i ) ) \sum\limits_{c[i]>0}(raw(i+1)-raw(i)) c[i]>0∑(raw(i+1)−raw(i)),令最终的答案 a n s ans ans 累加上 ( x 2 − x ) ∑ c [ i ] > 0 ( r a w ( i + 1 ) − r a w ( i ) ) (x_2-x)\sum\limits_{c[i]>0}(raw(i+1)-raw(i)) (x2−x)c[i]>0∑(raw(i+1)−raw(i))。
-
总复杂度 O ( n 2 ) O(n^2) O(n2)。
-
使用线段树维护 c c c 数组,复杂度优化至 O ( n log n ) O(n\log n) O(nlogn)。
-
可以不使用延迟标记。在线段树的每个节点上维护:节点代表区间被矩形覆盖长度 l e n len len,节点自身被覆盖的次数 c n t cnt cnt。对于四元组 ( x , y 1 , y 2 , k ) (x,y_1,y_2,k) (x,y1,y2,k),在 [ v a l ( y 1 ) , v a l ( y 2 ) − 1 ] [val(y_1),val(y_2)-1] [val(y1),val(y2)−1] 上执行区间修改,把这些节点的 c n t cnt cnt 加 k k k。
-
对于线段树任意一个节点 [ l , r ] [l,r] [l,r],若 c n t > 0 cnt>0 cnt>0,则 l e n len len 等于 r a w ( r + 1 ) − r a w ( l ) raw(r+1)-raw(l) raw(r+1)−raw(l)。否则,该点 l e n len len 等于两个子节点的 l e n len len 之和。在一个节点的 c n t cnt cnt 值被修改,以及线段树从下往上传递信息时,更新 l e n len len 值,根节点的 l e n len len 就是整个扫描线上被覆盖的长度。
const int N = 1;
struct ScanLine{
double x, y, z; int k;
inline bool operator < (const ScanLine &b) const { return x < b.x; }
} a[N*2];
struct Node{ int l, r, cnt; double len; } T[N*8];
map<double, int> val;
double raw[N*2];
int n, m;
inline void build(int p, int l, int r){
T[p].l = l, T[p].r = r, T[p].cnt = 0, T[p].len = 0;
int mid = l + r >> 1;
if(l != r) build(p << 1, l, mid), build(p << 1 | 1, mid + 1, r);
}
inline void change(int p, int l, int r, double k){
if(l <= T[p].l && T[p].r <= r){
if(T[p].cnt += k) T[p].len = raw[T[p].r+1] - raw[T[p].l];
else if(T[p].l == T[p].r) T[p].len = 0;
else T[p].len = T[p<<1].len + T[p<<1|1].len;
return;
}
int mid = T[p].l + T[p].r >> 1;
if(l <= mid) change(p << 1, l, r, k);
if(r > mid) change(p << 1 | 1, l, r, k);
if(T[p].cnt) T[p].len = raw[T[p].r+1] - raw[T[p].l];
else T[p].len = T[p<<1].len + T[p<<1|1].len;
}
inline double solve(){
scanf("%d", &n);
for(int i = 1; i <= n; ++ i){
double y, z;
scanf("%lld %lld %lld %lld", &a[i*2-1].x, &y, &a[i*2].x, &z);
raw[i*2-1] = a[i*2-1].y = a[i*2].y = y;
raw[i*2] = a[i*2-1].z = a[i*2].z = z;
a[i*2-1].k = 1, a[i*2].k = -1;
}
n <<= 1;
sort(raw + 1, raw + n + 1);
m = unique(raw + 1, raw + n + 1) - (raw + 1);
for(int i = 1; i <= m; ++ i) val[raw[i]] = i;
sort(a +1, a + n + 1);
build(1, 1, m - 1);
double ans = 0;
for(int i = 1; i < n; ++ i){
int y = val[a[i].y], z = val[a[i].z] - 1;
change(1, y, z, a[i].k);
ans += T[1].len * (a[i+1].x - a[i].x);
}
return ans;
}
动态开点 + 线段树合并
代码节选自P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并AC代码。
struct Node{ int l, r, dat, pos; } T[N*80]; int rt[N], cnt;
void init(){ for(int i = 1; i <= n; ++ i) rt[i] = ++ cnt; }
void pushup(int p){
T[p].dat = max(T[T[p].l].dat, T[T[p].r].dat);
T[p].pos = T[T[p].l].dat >= T[T[p].r].dat ? T[T[p].l].pos : T[T[p].r].pos;
}
void modify(int p, int l, int r, int pos, int val){
if(l == r){ T[p].dat += val, T[p].pos = T[p].dat ? l : 0; return; }
int mid = l + r >> 1;
if(pos <= mid){
if(!T[p].l) T[p].l = ++ cnt;
modify(T[p].l, l, mid, pos, val);
} else {
if(!T[p].r) T[p].r = ++ cnt;
modify(T[p].r, mid + 1, r, pos, val);
}
pushup(p);
}
int merge(int a, int b, int l, int r){
if(!a || !b) return a + b;
if(l == r){ T[a].dat += T[b].dat, T[a].pos = T[a].dat ? l : 0; return a; }
int mid = l + r >> 1;
T[a].l = merge(T[a].l, T[b].l, l, mid);
T[a].r = merge(T[a].r, T[b].r, mid + 1, r);
pushup(a); return a;
}
可持久化数组
const int N = 1e6 + 10;
struct PersistableSegmentTree{
#define MID (l+r>>1)
#define LSON T[x].l, l, MID
#define RSON T[x].r, MID+1, r
struct Node{ int l, r, val; } T[N*20];
int a[N*20], n, cnt, root[N];
int newnode(int x){ T[++cnt] = T[x]; return cnt; }
int buildtree(int x, int l, int r){
x = ++ cnt;
if(l == r){ T[x].val = a[l]; return x; }
T[x].l = buildtree(LSON);
T[x].r = buildtree(RSON);
return x;
}
int update(int x, int l, int r, int pos, int val){
x = newnode(x);
if(l == r){ T[x].val = val; return x; }
if(pos <= MID) T[x].l = update(LSON, pos, val);
else T[x].r = update(RSON, pos, val);
return x;
}
int query(int x, int l, int r, int pos){
if(l == r) return T[x].val;
if(pos <= MID) return query(LSON, pos);
else return query(RSON, pos);
}
void Init(){ root[0] = buildtree(0, 1, n); }
//v:查询/修改基于的版本
//i:新建版本编号
void Update(int v, int i, int pos, int val){
root[i] = update(root[v], 1, n, pos, val);
}
int Query(int v, int i, int pos){
return query(root[i] = root[v], 1, n, pos);
}
} Tree;
李超树
const int N = 1e5 + 10, T = 39989;
const double eps = 1e-9;
double k[N], b[N];
int tag[N*4], tot;
double calc(int id, int d){
return b[id] + k[id] * d;
}
void update(int p, int l, int r, int u){
int &v = tag[p], mid = l + r >> 1;
if(calc(u, mid) > calc(v, mid) + eps){
swap(u, v);
}
double t1 = calc(u, l) - calc(v, l), t2 = calc(u, r) - calc(v, r);
if(t1 > eps || (fabs(t1) < eps && u < v)){
update(p<<1, l, mid, u);
}
if(t2 > eps || (fabs(t2) < eps && u < v)){
update(p<<1|1, mid+1, r, u);
}
}
void fp(int p, int l, int r, int ql, int qr, int x){
if(ql <= l && r <= qr){
update(p, l, r, x);
} else {
int mid = l + r >> 1;
if(ql <= mid){
fp(p<<1, l, mid, ql, qr, x);
}
if(mid < qr){
fp(p<<1|1, mid+1, r, ql, qr, x);
}
}
}
void add(int x, int y, int xx, int yy){
++ tot;
if(x == xx){
k[tot] = 0;
b[tot] = max(y, yy);
} else {
k[tot] = 1.0 * (yy - y) / (xx - x);
b[tot] = y - k[tot] * x;
}
fp(1, 1, T, x, xx, tot);
}
pair<double, int> mx(pair<double, int> x, pair<double, int> y){
if(x.first + eps < y.first){
return y;
}
if(x.first > y.first + eps){
return x;
}
return x.second < y.second ? x : y;
}
pair<double, int> query(int p, int l, int r, int x){
if(x > r || x < l){
return make_pair(0, T+1);
}
int mid = l + r >> 1;
double res = calc(tag[p], x);
if(l == r){
return make_pair(res, tag[p]);
}
return mx(make_pair(res, tag[p]), mx(query(p<<1, l, mid, x), query(p<<1|1, mid+1, r, x)));
}