文章目录
一、线段树
1. 线段树基础
(1) 定义
#define _L k<<1 //左右子树
#define _R k<<1|1
#define L(x) tree[(x)].L //左右端点
#define R(x) tree[(x)].R
#define W(x) tree[(x)].w
#define T(x) tree[(x)].T //懒惰标记
struct node{
int L,R,w;
int T;
}tree[MAXN];
(2) 下传函数及建树
void down(int k){
T(_L) += T(k);
T(_R) += T(k);
W(_L) += T(k)*(R(_L)-L(_L)+1);
W(_R) += T(k)*(R(_R)-L(_R)+1);
T(k) = 0;
}
void build(int L, int R, int k){
L(k) = L;
R(k) = R;
if(L == R){
W(k) = 0;
return;
}
int mid = (L+R)>>1;
build(L ,mid,_L);
build(mid+1,R,_R);
W(k) = W(_L)+W(_R);
}
(3) 单点修改、单点查询
void update_point(int x, int k, int w){ //x 为目标点
if(L(k) == R(k)){
W(k) += w;
return;
}
down(k);
int mid = (L(k)+R(k))>>1;
if(x <= mid) update_point(x,_L,w);
else update_point(x,_R,w);
W(k) = W(_L)+W(_R);
}
int query_point(int x, int k){ //x 为目标点
if(L(k) == R(k)) return W(k);
down(k);
int mid = (L(k)+R(k))>>1;
if(x <= mid) return query_point(x,_L);
else return query_point(x,_R);
}
(4) 区间修改、区间查询
void update_interval(int l, int r, int k, int w){ //l,r 为目标区间
if(L(k)>=l && R(k)<=r){
W(k) += w*(R(k)-L(k)+1);
T(k) += w;
return;
}
down(k);
int mid = (L(k)+R(k))>>1;
if(l <= mid) update_interval(l,r,_L,w);
if(r > mid) update_interval(l,r,_R,w);
W(k) = W(_L)+W(_R);
}
int query_interval(int l, int r, int k){
int ans = 0;
if(L(k)>=l && R(k)<=r) return W(k);
down(k);
int mid = (L(k)+R(k))>>1;
if(L <= mid) ans = query_interval(l,r,_L);
if(R > mid) ans += query_interval(l,r,_R);
return ans;
}
2. 线段树求区间最值
(1) 下传函数及建树
void down(int k){
T(_L) += T(k); W(_L) += T(k);
T(_R) += T(k); W(_R) += T(k);
T(k) = 0;
}
void build_max(int L, int R, int k){
L(k) = L;
R(k) = R;
if(L == R){
W(k) = 0;
return;
}
int mid = (L(k)+R(k))>>1;
build_max(L ,mid,_L);
build_max(mid+1,R,_R);
W(k) = max(W(_L),W(_R));
}
(2) 单点修改
void update_point_max(int x, int k, int w){ //x 为目标点
if(L(k) == R(k)){
W(k) += w;
return;
}
down(k);
int mid = (L(k)+R(k))>>1;
if(x <= mid) update_point_max(x,_L,w);
else update_point_max(x,_R,w);
W(k) = max(W(_L),W(_R));
}
(3) 区间修改、区间查询
void upadate_interval_max(int l, int r, int k, int w){ //l,r 为目标区间
if(L(k)>=l && R(k)<=r){
W(k) += w;
T(k) += w;
return;
}
down(k);
int mid = (L(k)+R(k))>>1;
if(l <= mid) upadate_interval_max(l,r,_L,w);
if(r > mid) upadate_interval_max(l,r,_R,w);
W(k) = max(W(_L),W(_R));
}
int query_interval_max(int l, int r, int k){
int x=0,y=0;
if(L(k)>=l && R(k)<=r) return W(k);
down(k);
int mid = (L(k)+R(k))>>1;
if(l <= mid) x = query_interval_max(l,r,_L);
if(r > mid) y = query_interval_max(l,r,_R);
return max(x,y);
}
3. 线段树求区间连续最大子段和
SP1716 GSS3 - Can you answer these queries III
(1) 定义
#define mL(x) tree[(x)].maxL // 最大前缀和
#define mR(x) tree[(x)].maxR // 最大后缀和
#define mS(x) tree[(x)].maxS // 最大连续子段和
#define sm(x) tree[(x)].sum // 区间和
struct node{
int L,R;
int maxL,maxR,maxS,sum;
}tree[MAXN];
(2) 更新函数及建树
void update_tree_sum(int k){
sm(k) = sm(_L)+sm(_R);
mL(k) = max(mL(_L),sm(_L)+mL(_R));
mR(k) = max(mR(_R),sm(_R)+mR(_L));
mS(k) = max(max(mS(_L),mS(_R)),mL(_R)+mR(_L));
}
void build_sum(int L, int R, int k, int i){ //a[i] 为原数列
L(k) = L;
R(k) = R;
if(L == R){
mL(k) = mR(k) = mS(k) = sm(k) = a[i];
return;
}
int mid = (L(k)+R(k))>>1;
build_sum(L ,mid,_L,i);
build_sum(mid+1,R,_R,i);
update_tree_sum(k);
}
(3) 单点修改
void update_point_sum(int x, int k, int w){
if(L(k) == R(k)){
mL(k) = mR(k) = mS(k) = sm(k) = w;
return;
}
int mid = (L(k)+R(k))>>1;
if(x <= mid) update_point_sum(x,_L,w);
else update_point_sum(x,_R,w);
update_tree_sum(k);
}
(4) 区间查询
node query_interval_sum(int l, int r, int k){
node Ls,Rs,ans;
if(L(k)>=l && R(k)<=r) return tree[k];
int mid = (L(k)+R(k))/2;
if(r <= mid) return query_interval_sum(L,R,_L);
else if(l > mid) return query_interval_sum(L,R,_R);
else{
Ls = query_interval_sum(L,R,_L);
Rs = query_interval_sum(L,R,_R);
ans.sum = Ls.sum+Rs.sum;
ans.maxL = max(Ls.maxL,Ls.sum+Rs.maxL);
ans.maxR = max(Rs.maxR,Rs.sum+Ls.maxR);
ans.maxS = max(max(Ls.maxS,Rs.maxS),Ls.maxR+Rs.maxL);
return ans;
}
}
4. 区间平均数、方差
(1) 定义
#define S(x) tree[(x)].S;
struct node{
int L,R,T;
int S,sum; //平方和,区间和
}tree[MAXN];
(2) 下传函数、更新函数及建树
void down(int k){
int T = T(k);
T(_L) += T;
T(_R) += T;
S(_L) = (R(_L)-L(_L)+1)*T*T+2*T*sm(_L);
S(_R) = (R(_R)-L(_L)+1)*T*T+2*T*sm(_R);
sm(_L) = (R(_L)-L(_L)+1)*T;
sm(_R) = (R(_R)-L(_R)+1)*T;
T(k) = 0;
}
void update_tree_S(int k){
sm(k) = sm(_L)+sm(_R);
S (k) = S (_L)+S (_R);
}
void build_tree_S(int L, int R, int k, int i){
L(k) = L;
R(k) = R;
if(L == R){
sm(k) = a[i];
S (k) = a[i]*a[i];
return;
}
int mid = (L(k)+R(k))>>1;
build_tree_S(L ,mid,_L,i);
build_tree_S(mid+1,R,_R,i);
update_tree_S(k);
}
(3) 区间修改、区间查询
void update_interval_S(int l ,int r, int k, int w){
if(L(k)>=l && R(k)<=r){
S (k) += w*(R-L+1)*w+2*w*sm(k);
sm(k) += w*(R-L+1);
T (k) += w;
return;
}
down(k);
int mid = (L(k)+R(k))>>1;
if(l <= mid) update_interval_S(l,r,_L,w);
if(r > mid) update_interval_S(l,r,_R,w);
update_tree_S(k);
}
node query_interval_S(int l, int r, int k){
node x,y,ans;
if(L(k)>=l && R(k)<=r) return tree[k];
down(k);
int mid = (L(k)+R(k))>>1;
if(l <= mid) x = query_interval_S(l,r,_L);
if(r > mid) y = query_interval_S(l,r,_R);
ans.sum = x.sum+y.sum;
ans.S = x.S+y.S;
}
5. 线段树求最长严格上升子序列(subsequence)
(1) 定义
#define Mx(x) tree[(x)].Max
#define Ln(x) tree[(x)].len
struct node{
int L,R;
int Max,len; //区间最值,子序列长度
}tree[MAXN];
(2) 更新函数及建树
int update_tree_sub(int k, int w){
if(L(k) == R(k)) return Mx(k) > w;
if(w >= Mx(_L)) return update_tree_sub(_R,w);
return Ln(k)-Ln(_L)+update_tree_sub(_L,w);
}
void build_tree_sub(int L, int R, int k){
L(k) = L;
R(k) = R;
if(L == R) return;
int mid = (L(k)+R(k))>>1;
build_tree_sub(L,mid,_L);
build_tree_sub(mid+1,R,_R);
}
(3) 单点修改
void update_point_sub(int x, int k, int w){
if(L(k) == R(k)){
Mx(k) = w;
Ln(k) = 1;
return;
}
int mid = (L(k)+R(k))>>1;
if(x >= mid) update_point_sub(x,_L,w);
if(x < mid) update_point_sub(x,_R,w);
Mx(k) = max(Mx(_L),Mx(_R));
Ln(k) = Ln(_L)+update_tree_sub(_R,Mx(_L));
}
6. 区间修改转单点修改
利用差分,再维护一个区间前缀和就可以了
7. XOR
区间修改
区间查询:1 的个数
我不会。
8. 线段树求第 k 个值
利用权值线段树的思想,当这个点有值时 t r e e [ k ] . c n t tree[k].cnt tree[k].cnt 为 1 ,统计它的和,查询第 k k k 个值时若 k < = t r e e [ _ L ] . c n t k <= tree[\_L].cnt k<=tree[_L].cnt ,说明第 k k k 个数在左边,否则在右边。
注意,查询右子树时要将 k k k 减去 t r e e [ _ L ] . c n t t r e e [ _ L ] . c n t tree[\_L].cnttree[\_L].cnt tree[_L].cnttree[_L].cnt 。
(1) 定义
#define int long long
struct node{
int L,R;
int w,cnt;
}tree[MAXN];
(2) 更新函数与建树
void update(int k){
W(k) = W(_L)+W(_R);
tree[k].cnt = tree[_L].cnt+tree[_R].cnt;
}
void build(int L, int R, int k){
L(k) = L;
R(k) = R;
if(L == R){
W(k) = a[L];
if(a[L]) tree[k].cnt = 1;
return;
}
int mid = (L+R)>>1;
build(L,mid,_L);
build(mid+1,R,_R);
update(k);
}
(3) 单点更新、添加、删除
void update_point(int k, int x, int w){
if(L(k) == R(k)){
W(k) -= w;
return;
}
int mid = (L(k)+R(k))>>1;
if(x <= mid) update_point(_L,x,w);
else update_point(_R,x,w);
update(k);
}
void add_point(int k, int x, int w){
if(L(k) == R(k)){
W(k) = w;
tree[k].cnt = 1;
return;
}
int mid = (L(k)+R(k))>>1;
if(x <= mid) add_point(_L,x,w);
else add_point(_R,x,w);
update(k);
}
void Delete(int k, int x){
if(L(k)==R(k) && x==tree[k].cnt){
W(k) = 0;
tree[k].cnt--;
return;
}
if(x <= tree[_L].cnt) Delete(_L,x);
else Delete(_R,x-tree[_L].cnt);
update(k);
}
二、线段树合并(动态开点线段树)
#include <stdio.h>
#include <iostream>
using namespace std;
#define MAXN 3000005
#define Ls(x) tree[(x)].ls
#define Rs(x) tree[(x)].rs
#define L(x) tree[(x)].L
#define R(x) tree[(x)].R
int n,m,cnt;
int prn[MAXN],root[MAXN];
struct node{
int ls,rs.L,R,w,id;
}tree[4*MAXN];
int find(int x){
if(prn[x] == x) return x;
return prn[x] = find(prn[x]);
}
int build(int k, int l, int r, int rk, int i){
if(!k) k = ++cnt;
L(k) = l;
R(k) = r;
if(l == r){
tree[k].id = i;
tree[k].w = 1;
return k;
}
int mid = (l+r)>>1;
if(rk <= mid) Ls(k) = build(Ls(k),l ,mid,rk,i);
else Rs(k) = build(Rs(k),mid+1,r,rk,i);
tree[k].w = tree[Ls(k)].w+tree[Rs(k)].w;
return k;
}
int merge(int x, int y){
if(!x) return y;
if(!y) return x;
if(L(x) == R(x)){
tree[x].w += tree[y].w;
if(!tree[x].id)
tree[x].id = tree[y].id;
return x;
}
Ls(x) = merge(Ls(x),Ls(y));
Rs(x) = merge(Rs(x),Rs(y));
tree[x].w = tree[Ls(x)].w+tree[Rs(x)].w;
return x;
}
int query(int k, int rk){
if(tree[k].w<rk || !k) return -1;
if(L(k) == R(k)) return tree[k].id;
if(tree[Ls(k)].w >= rk) return query(Ls(k),rk);
else return query(Rs(k),rk-tree[Ls(k)].w);
}
int main(void){
int x,y;
cin >> n >> m;
for(int i=1;i<=n;i++){
cin >> x;
prn[i] = i;
root[i] = build(root[i],1,n,x,i);
}
for(int i=1;i<=m;i++){
cin >> x >> y;
int t1 = find(x);
int t2 = find(y);
prn[t2] = t1;
root[t1] = merge(root[t1],root[t2]);
}
cin >> m;
for(int i=1;i<=m;i++){
char p;
cin >> p >> x >> y;
if(p == 'Q'){
int t1 = find(x);
cout << query(root[t1],y) << endl;
}
if(p == 'B'){
int t1 = find(x);
int t2 = find(y);
if(t1 == t2) continue;
prn[t2] = t1;
root[t1] = merge(root[t1],root[t2]);
}
}
return 0;
}