郝玩的資料結構——線段樹(待upd)

cathy_zro發表於2024-11-23

線段樹,是一種支援點修點查,去修區查的高階資料結構,單詞操作時間複雜度為O(log2點數),非常的優秀
拉張圖來解釋一下線段樹:

每個父節點的權值是兩個子節點權值的和
好的。首先建一棵線段樹我們來採用遞迴建樹:先從根節點DFS遍歷,然後返回後使用push_up函式累加——這樣就可以保證線段樹的性質
其次就是修改
如果是單點修改的話就從根節點向下找到該節點然後返回的時候沿途更新其祖先
如果是區間修改的話
引入:懶標記:LAZYTAG
如果一個個修改的話複雜度很高,所以當修改時應該找到比所選區間小或相等的一個或者幾個區間,並使區間的交集為空,並集為所選區間
然後就是查詢
單點查詢就是從線段樹的根走到最末一層,不多說
區間查詢
和區修極其神似的,區查就是選幾個。。。(上面有)的區間,累加和,作為答案,輸出
想必透過我的解釋,你一定沒懂,啊不,懂了,所以,讓我們來看程式碼——板子題,以及板子題的微調(板子還有個區間乘加,可是老登筆者還沒有Ac(沒做))

哦對了,線段樹碼量真多,(當然也有老登筆者菜的原因),但這不是重點

線段樹一定要開4倍陣列!!!!

點選檢視程式碼
#include<bits/stdc++.h>
#define lc p<<1
#define rc p<<1|1
#define int long long
using namespace std;
inline int read(){
	int nb=0,f=1;
	char c=getchar();
	while(c>'9' || c<'0'){
		if(c=='-'){
			f=-f;
		}
		c=getchar();
	}
	while(c>='0' && c<='9'){
		nb=nb*10+c-'0';
		c=getchar();
	}
	return nb*f;
}
const int N=1e6+145;

struct nb{
	int l,r,sum,tag;
	nb(int a,int b,int c,int d):l(a),r(b),sum(c),tag(d){}
	nb(){}
}tr[N<<2];
int n,q,wq,l,r,x;
int a[N];

inline void push_up(int p){
	tr[p].sum=tr[lc].sum+tr[rc].sum;
}

inline void push_down(int p){
	if(tr[p].tag){
		tr[lc].tag+=tr[p].tag;
		tr[rc].tag+=tr[p].tag;
		tr[lc].sum+=tr[p].tag*(tr[lc].r-tr[lc].l+1);
		tr[rc].sum+=tr[p].tag*(tr[rc].r-tr[rc].l+1);
		tr[p].tag=0;
	}
}

inline void bt(int p,int l,int r){//buildtree
	tr[p]={l,r,a[l],0};
	if(l==r){
		return ;
	}
	int mid=(l+r)>>1;
	bt(lc,l,mid);
	bt(rc,mid+1,r);
	push_up(p);
}

inline void wlgd(int p,int l,int r,int k){
	if(tr[p].l>=l && tr[p].r<=r){
		tr[p].tag+=k;
		tr[p].sum+=k*(tr[p].r-tr[p].l+1);
		return ;
	}
	int mid=tr[p].l+tr[p].r>>1;
	push_down(p);
	if(mid>=l) wlgd(lc,l,r,k);
	if(mid<r) wlgd(rc,l,r,k);
	push_up(p);
}

inline int query(int p,int l,int r){
	if(l<=tr[p].l && r>=tr[p].r){
		return tr[p].sum;
	}
	int res=0;
	int mid=tr[p].l+tr[p].r>>1;
	push_down(p);
	if(mid>=l) res+=query(lc,l,r);
	if(mid<r) res+=query(rc,l,r);
	return res;
}
signed main(){
	n=read(),q=read();
	for(int i=1;i<=n;i++){
		a[i]=read();
	}
	bt(1,1,n);
//	for(int i=1;i<=2*n;i++){
//		cout<<tr[i].sum<<"  ";
//	}
	for(int i=1;i<=q;i++){
		cin>>wq;
		if(wq==1){
			l=read(),r=read(),x=read();
		//cin>>l>>r>>x;
			wlgd(1,l,r,x);
		}
		else{
			l=read(),r=read();
			//cin>>l>>r;
			cout<<query(1,l,r)<<"\n";
		}
	}
	return 0;
}

板子題1

點選檢視程式碼
#include<bits/stdc++.h>
#define lc p<<1
#define rc p<<1|1
#define int long long
using namespace std;
int n,m,op,ll,rr;
const int N=2e5+10;
char c[N];
struct node{
	int sum,tag,l,r;
}tr[N<<2];

void push_up(int p){
	tr[p].sum=tr[lc].sum+tr[rc].sum;
}

void push_down(int p){
	if(tr[p].tag){
		tr[lc].tag^=1;
		tr[rc].tag^=1;
		tr[lc].sum=tr[lc].r-tr[lc].l+1-tr[lc].sum;
		tr[rc].sum=tr[rc].r-tr[rc].l+1-tr[rc].sum;
		tr[p].tag=0;
	}
}

void buildtree(int p,int l,int r){
	tr[p]={0,0,l,r};
	if(l==r){
		if(c[l]=='1'){
			tr[p].sum++;
		}
		return ;
	}
	int mid=l+r>>1;
	buildtree(lc,l,mid);
	buildtree(rc,mid+1,r);
	push_up(p);
}

void update(int p,int l,int r){
	if(tr[p].l>=l && tr[p].r<=r){
		tr[p].tag^=1;
		tr[p].sum=tr[p].r-tr[p].l+1-tr[p].sum;
		return ;
	}
	int mid=tr[p].l+tr[p].r>>1;
	push_down(p);
	if(mid>=l) update(lc,l,r);
	if(mid<r) update(rc,l,r);
	push_up(p);
}

int query(int p,int l,int r){
	int res=0;
	if(tr[p].l>=l && tr[p].r<=r){
		return tr[p].sum;
	}
	push_down(p);
	int mid=tr[p].l+tr[p].r>>1;
	if(mid>=l) res+=query(lc,l,r);
	if(mid<r) res+=query(rc,l,r);
	return res;
}

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>c[i];
	}
	buildtree(1,1,n);
	for(int i=1;i<=m;i++){
		cin>>op>>ll>>rr;
		if(op==0){
			update(1,ll,rr);
		}
		else{
			cout<<query(1,ll,rr)<<"\n";
		}
	}
	return 0;
}

一點小進階

好的,哪天閒的沒事再來更個點修點查啥的
不過想必聰明的你會了區修區查之後一定也會點修點查了
拜拜~

相關文章