線段樹學習筆記
阿新 • • 發佈:2020-08-21
線段樹學習總結
概念
每個節點以結構體的方式儲存,結構體包含以下幾個資訊:
區間左端點、右端點;(這兩者必有)
這個區間要維護的資訊(事實際情況而定,數目不等)。
圖例:
性質
1、每個節點的左孩子區間範圍為[l,mid],右孩子為[mid+1,r]
2、對於結點k,左孩子結點為2k,右孩子為2k+1,這符合完全二叉樹的性質
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
建樹
基本結構:
a.對於二分到的每一個結點,給它的左右端點確定範圍。
b.如果是葉子節點,儲存要維護的資訊。
c.累加。
參考程式碼
struct node{ int l,r,w; }tree[100010]; inline void Build(int l,int r,int k){ tree[k].l = l,tree[k].r = r; if(l == r){ scanf("%d",&tree[k].w); return; } int mid = (tree[k].l + tree[k].r) / 2; Build(l,mid,k * 2);//遞迴 Build(mid + 1,r,k * 2 + 1);//遞迴 tree[k].w = tree[k * 2].w + tree[k * 2 + 1].w;//累加 return; }
注意事項
1.開結構體陣列時要開4倍
2.在輸入完葉子結點後要return,因為葉子結點不用再遞迴了。
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
單點查詢
基本結構:
與二分查詢法基本一致,如果當前列舉的點左右端點相等,即葉子節點,就是目標節點。否則設查詢位置為x,當前結點區間範圍為l,r,中點為mid。則如果x<=mid,就遞迴它的左孩子,否則遞迴它的右孩子
參考程式碼
inline int Ask_p(int x){ if(tree[x].l == tree[x].r){//葉子結點 return tree[x].w; } int mid = (tree[x].l + tree[x].r) / 2; if(mid >= x) Ask_p(x * 2);//目標位置比中點靠左,就遞迴左孩子 else Ask_p(x * 2 + 1);//遞迴右孩子 }
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
單點修改
基本結構:
用單點查詢的方法找到目標節點並修改它的權值
參考程式碼
inline void change(int x,int y,int k){
if(tree[k].l == tree[k].r){//找到目標節點
tree[k].w = y;//xiu'gai'quan'zhi
return;
}
int mid = (tree[k].l + tree[k].r) / 2;
if(mid >= x)
change(x,y,k * 2);
else change(x,y,k * 2 + 1);
tree[k].w = tree[k * 2].w + tree[k * 2 + 1].w;
return;
}
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
區間查詢
基本結構:
分情況來討論:
假設l,r為節點區間,x,y為目標區間,則有一下三種情況:
一:
二:
三:
參考程式碼
inline int Ask_section(int x,int y,int k){
if(tree[k].l > y || tree[k].r < x)return 0;
if(tree[k].l >= x && tree[k].r <= y){
return tree[k].w;
}
int mid = (tree[k].l + tree[k].r) / 2;
return Ask_section(x,y,k * 2) + Ask_section(x,y,k * 2 + 1);
}
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
完整程式碼在此(乘法和加法)
#include <bits/stdc++.h>
using namespace std;
struct node{
long long w,l,r;
long long tag_add = 0,tag_mul = 1;
}tree[600010]; //4 times;
long long ans,n,m,p;
void build(long long l,long long r,long long num){
tree[num].l = l;tree[num].r = r;
tree[num].tag_mul = 1;
if(tree[num].l == tree[num].r){
cin>>tree[num].w;
tree[num].w %= p;
return;
}
long long mid = (tree[num].l + tree[num].r) / 2;
build(l,mid,num * 2);
build(mid + 1,r,num * 2 + 1);
tree[num].w = (tree[num * 2].w + tree[num * 2 + 1].w) % p;
return;
}
void pushdown(long long num){
tree[num << 1].w = (tree[num << 1].w * tree[num].tag_mul + tree[num].tag_add * (tree[num << 1].r - tree[num << 1].l + 1)) % p;
tree[num << 1 | 1].w = (tree[num << 1 | 1].w * tree[num].tag_mul + tree[num].tag_add * (tree[num << 1 | 1].r - tree[num << 1 | 1].l + 1)) % p;
tree[num << 1].tag_mul *= tree[num].tag_mul;
tree[num << 1].tag_mul %= p;
tree[num << 1 | 1].tag_mul *= tree[num].tag_mul;
tree[num << 1 | 1].tag_mul %= p;
tree[num << 1].tag_add = (tree[num << 1].tag_add * tree[num].tag_mul + tree[num].tag_add) % p;
tree[num << 1 | 1].tag_add = (tree[num << 1 | 1].tag_add * tree[num].tag_mul + tree[num].tag_add) % p;
tree[num].tag_mul = 1;
tree[num].tag_add = 0;
return;
}
void ask_a(long long num,long long tar_l,long long tar_r){
if(tree[num].l >= tar_l && tree[num].r <= tar_r){
ans += tree[num].w;
ans %= p;
return;
}
pushdown(num);
long long mid = (tree[num].l + tree[num].r) / 2;
if(mid >= tar_l)
ask_a(num * 2,tar_l,tar_r);
if(mid < tar_r)
ask_a(num * 2 + 1,tar_l,tar_r);
}
void add_a_mul(long long num,long long tar_l,long long tar_r,long long w){
if(tree[num].l >= tar_l && tree[num].r <= tar_r){
tree[num].tag_mul *= w;
tree[num].tag_mul %= p;
tree[num].w *= w;
tree[num].w %= p;
tree[num].tag_add *= w;
tree[num].tag_add %= p;
return;
}
pushdown(num);
long long mid = (tree[num].l + tree[num].r) / 2;
if(mid >= tar_l)
add_a_mul(num * 2,tar_l,tar_r,w);
if(mid < tar_r)
add_a_mul(num * 2 + 1,tar_l,tar_r,w);
tree[num].w = (tree[num * 2].w + tree[num * 2 + 1].w) % p;
}
void add_a_add(long long num,long long tar_l,long long tar_r,long long w){
if(tree[num].l >= tar_l && tree[num].r <= tar_r){
tree[num].w += (tree[num].r - tree[num].l + 1) * w;
tree[num].w %= p;
tree[num].tag_add += w;
tree[num].tag_add %= p;
return;
}
pushdown(num);
long long mid = (tree[num].l + tree[num].r) / 2;
if(mid >= tar_l)
add_a_add(num * 2,tar_l,tar_r,w);
if(mid < tar_r)
add_a_add(num * 2 + 1,tar_l,tar_r,w);
tree[num].w = (tree[num * 2].w + tree[num * 2 + 1].w) % p;
}
int main(){
cin>>n>>m>>p;
build(1,n,1);
for(int i = 1;i <= m;i++){
int opt;
cin>>opt;
if(opt == 1){
int x,y,w;
cin>>x>>y>>w;
add_a_mul(1,x,y,w);
}
if(opt == 2){
int x,y,w;
cin>>x>>y>>w;
add_a_add(1,x,y,w);
}
if(opt == 3){
int x,y;
cin>>x>>y;
ans = 0;
ask_a(1,x,y);
ans %= p;
cout<<ans<<endl;
}
}
return 0;
}