沙雕关于线段树的一点总结(滑稽)
这段时间(好吧其实只有昨天)写了一点线段树,虽然我是个蒟蒻啥都不会只会傻敷敷的网上去复制别人的代码但是经过我长时间的观察还是有一点点收获的滑稽。
先变成我头部的颜色
一般呢,在写线段树的时候 k,l,r 即序号,左右边界是分不开的,其他参数看题目不同而自行决腚。
如果你这三个不凑齐还能AC我就把电脑屏幕吃了
好吧进入正题
本人学习用的是信息学奥赛一本通,感觉还行,但是没有漫画好看
一本通上常用的写法有以下几种(在几种不同的情境下) 建树过程没什么好说的
1.求max或者min(此处以min为例)
一.询问
int query(int k,int l,int r,int x,int y) { if(y<l||x>r) return 233333333; if(x<=l&&r<=y) return Min[k]; int mid=l+r>>1; return min(query(k<<1,l,mid,x,y),query(k<<1|1,mid+1,r,x,y)); }
二.修改(单点修改)
void change(int k,int l,int r,int x,int v) { if(r<x||l>x) return ; if(l==r&&l==x) { Min[k]=v; return ; } int mid=l+r>>1; change(k<<1,l,mid,x,v); change(k<<1|1,mid+1,r,x,v); Min[k]=min(Min[k<<1],Min[k<<1|1]); }
敲得比较快不过问题不大反正没人看(滑稽)
好吧我比较懒接下来上几道ybtOJ的题目再来体会一下
忽略这个书签
1547 区间和
#include<bits/stdc++.h> using namespace std; const int N=100001; int n,m; long long sum[N*4]; int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=x*10+ch-'0'; ch=getchar(); } return x*f; } void change(int k,int l,int r,int p,int v) { if(r<p||l>p) return ; if(l==r&&l==p) { sum[k]+=v; return ; } int mid=l+r>>1; change(k*2,l,mid,p,v); change(k*2+1,mid+1,r,p,v); sum[k]=sum[k*2]+sum[k*2+1]; } long long query(int k,int l,int r,int x,int y) { if(y<l||x>r) return 0; if(l>=x&&r<=y) return sum[k]; int mid=l+r>>1; long long ans=0; ans=query(k*2,l,mid,x,y); ans+=query(k*2+1,mid+1,r,x,y); return ans; } int main() { n=read();m=read(); int k,l,r; for(int i=1;i<=m;i++) { k=read();l=read();r=read(); if(k==0) change(1,1,n,l,r); else printf("%lld\n",query(1,1,n,l,r)); } return 0; }
以及1548:【例 2】A Simple Problem with Integers
#include<bits/stdc++.h> using namespace std; const int N=1e6+1; long long sum[N*4],add[N*4]; int a[N],n,q; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=x*10+ch-'0'; ch=getchar(); } return x*f; } void build(int k,int l,int r) { if(l==r) { sum[k]=a[l]; return ; } int mid=l+r>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); sum[k]=sum[k<<1]+sum[k<<1|1]; } void Add(int k,int l,int r,int v) { add[k]+=v; sum[k]+=(long long)v*(r-l+1); } void pushdown(int k,int l,int r,int mid) { if(add[k]==0) return ; Add(k<<1,l,mid,add[k]); Add(k<<1|1,mid+1,r,add[k]); add[k]=0; } long long query(int k,int l,int r,int x,int y) { if(l>=x&&r<=y) return sum[k]; int mid=l+r>>1; long long ans=0; pushdown(k,l,r,mid); if(x<=mid) ans+=query(k<<1,l,mid,x,y); if(y>mid) ans+=query(k<<1|1,mid+1,r,x,y); return ans; } void change(int k,int l,int r,int x,int y,int v) { if(l>=x&&r<=y) return Add(k,l,r,v); int mid=l+r>>1; pushdown(k,l,r,mid); if(x<=mid) change(k<<1,l,mid,x,y,v); if(y>mid) change(k<<1|1,mid+1,r,x,y,v); sum[k]=sum[k<<1]+sum[k<<1|1]; } int main() { n=read();q=read(); for(int i=1;i<=n;i++) a[i]=read(); build(1,1,n); int way,L,R,v; for(int i=1;i<=q;i++) { way=read(); if(way==1) { L=read();R=read();v=read(); change(1,1,n,L,R,v); } else { L=read(); R=read();printf("%lld\n",query(1,1,n,L,R)); } } return 0; }
由此可见(以上都是例题)ybt上的线段树代码不会刻意去压缩寻找的区间(即减少了过多的if语句判断条件)
这样可以降低编程时的复杂度而且对O的影响也不大但是可能可读性就会差一些
而且部分题目如果使用这种方法会很棘手
#include<bits/stdc++.h> #define ll long long using namespace std; const int N=1e5+1; int n,m; struct Node{ ll sum; bool flag; }t[N<<2]; int a[N]; inline void build(int k,int l,int r) { if(l==r) { t[k].sum=(ll)a[l]; if(t[k].sum==0||t[k].sum==1) t[k].flag=1; return ; } int mid=l+r>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); t[k].sum=t[k<<1].sum+t[k<<1|1].sum; t[k].flag=t[k<<1].flag&t[k<<1|1].flag; } inline long long query(int k,int l,int r,int x,int y) { if(l>y||r<x) return 0; if(l>=x&&r<=y) return t[k].sum; int mid=l+r>>1; long long ans=0; if(x<=mid) ans+=query(k<<1,l,mid,x,y); if(y>mid) ans+=query(k<<1|1,mid+1,r,x,y); return ans; } inline void change(int k,int l,int r,int x,int y) { if(t[k].flag) return ; if(l==r) { t[k].sum=(ll)sqrt(t[k].sum); if(t[k].sum==1||t[k].sum==0) t[k].flag=1; return ; } int mid=l+r>>1; if(x>mid) change(k<<1|1,mid+1,r,x,y); else if(y<=mid) change(k<<1,l,mid,x,y); else { change(k<<1,l,mid,x,y); change(k<<1|1,mid+1,r,x,y); } t[k].sum=t[k<<1].sum+t[k<<1|1].sum; t[k].flag=t[k<<1].flag&t[k<<1|1].flag; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); build(1,1,n); scanf("%d",&m); int a,b,c; for(int i=1;i<=m;i++) { scanf("%d%d%d",&a,&b,&c); if(a==1) printf("%lld\n",query(1,1,n,b,c)); else change(1,1,n,b,c); } return 0; }
这道题目是BZOJ3211 花神游历各国
这道题在修改过程中如果加上if语句判定来压缩寻找的区间就会方便很多
扯完了