有关线段树的各种版本我也是从去年结束OI之后就再也没有碰过了,现在看到各种树觉得也是无从下手晕的很,拖拖拉拉一直三四天才弄出来一个专题。
重新扒出来看一眼各种树怎么写,然后写写博客记录一下心里历程(无言吐槽)
言归正传,先来说一下我对线段树这个美好东西的理解。
1.其实我觉得最简单的就是线段树的板子
2.如果你看一道题看一份题解看不懂然后人家告诉你这是线段树你说“怎么可能这是线段树?” 这种现象非常正常。
线段树专题一:单点更新
题目形如:求某个区间内的最大值,更新某个节点之后求和等。总之就是不涉猎区间更新的问题(其实也就是TLE的问题,不然不TLE谁去用lazytag)
I hate it(hdu 1754) 题目描述: 很多学校流行一种比较的习惯。老师们很喜欢询问,从某某到某某当中,分数最高的是多少。 这让很多学生很反感。不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问。当然,老师有时候需要更新某位同学的成绩。 本题目包含多组测试 在每个测试的第一行,有两个正整数 N 和 M ( 0~N<=200000,0~M<5000 ),分别代表学生的数目和操作的数目。
学生ID编号分别从1编到N。第二行包含N个整数,代表这N个学生的初始成绩,其中第i个数代表ID为i的学生的成绩。接下来有M行。每一行有一个字符 C (只取’Q’或’U’) ,和两个正整数A,B。 当C为’Q’的时候,表示这是一条询问操作,它询问ID从A到B(包括A,B)的学生当中,成绩最高的是多少。 当C为’U’的时候,表示这是一条更新操作,要求把ID为A的学生的成绩更改为B。 对于每一次询问操作,在一行里面输出最高成绩。 输入 本题目包含多组测试,请处理到文件结束。 在每个测试的第一行,有两个正整数 N 和 M 分别代表学生的数目和操作的数目。 学生ID编号分别从1编到N。 第二行包含N个整数,代表这N个学生的初始成绩,其中第i个数代表ID为i的学生的成绩。 接下来有M行。每一行有一个字符 C (只取’Q’或’U’) ,和两个正整数A,B。 当C为’Q’的时候,表示这是一条询问操作,它询问ID从A到B(包括A,B)的学生当中,成绩最高的是多少。 当C为’U’的时候,表示这是一条更新操作,要求把ID为A的学生的成绩更改为B。 输出 对于每一次询问操作,在一行里面输出最高成绩。 样例输入 5 6 1 2 3 4 5 Q 1 5 U 3 6 Q 3 4 Q 4 5 U 2 9 Q 1 5 样例输出 5 6 5 9 ---------------------
#include<cstdio> #include<iostream> using namespace std; const int MAXX = 200000; struct node{ int l,r,val; }tree[MAXX*4+5]; int a[MAXX+5]; int max(int x,int y){ if(x>y){ return x; }else{ return y; } } int build(int p,int l,int r){ tree[p].l=l; tree[p].r=r; if(l == r){ tree[p].val = a[l]; } int mid = (l+r)/2; int x1 = build(p<<1,l,mid); int x2 = build(p<<1|1,mid+1,r); tree[p].val = max(x1,x2); return tree[p].val; } int update(int p,int x){ if(x<tree[p].l || x>tree[p].r) return tree[p].val; if(tree[p].l==tree[p].r) { tree[p].val=a[tree[p].l]; return tree[p].val; } int x1=update(2*p,x); int x2=update(2*p+1,x); tree[p].val=max(x1,x2); return tree[p].val; } int query(int p,int x,int y)//查询 { if(y<tree[p].l || x>tree[p].r) return 0; if(x<=tree[p].l && tree[p].r<=y) return tree[p].val; int x1=query(2*p,x,y); int x2=query(2*p+1,x,y); return max(x1,x2); } int main(){ int i,j,k,m,n;int x,y;char c; scanf("%d%d",&n,&m); for(i=1;i<=n;i++) scanf("%d",&a[i]); create_tree(1,1,n); for(i=1;i<=m;i++) { getchar(); scanf("%c%d%d",&c,&x,&y); if(c=='Q') {printf("%d\n",query(1,x,y));} else {a[x]=y;update(1,x);} } return 0; }
线段树专题二:区间更新
这里就用lazytag了,不用会TLE..比如luogu的T3372
#include<cstdio> #include<iostream> using namespace std; const int MAXX = 100000; struct node{ int l,r; long long val,laz; }tree[MAXX*4+2]; int a[MAXX+5]; void build(int p,int l,int r){ tree[p].l = l; tree[p].r = r; if(l == r){ tree[p].val = a[l]; return; } int mid = (l+r)/2; build(p<<1,l,mid); build(p<<1|1,mid+1,r); tree[p].val = tree[p<<1].val + tree[p<<1|1].val; } void pushdown(int p){ if(tree[p].laz != 0){ tree[p<<1].val += tree[p].laz*(tree[p<<1].r-tree[p<<1].l+1); tree[p<<1|1].val += tree[p].laz*(tree[p<<1|1].r-tree[p<<1|1].l+1); tree[p<<1].laz += tree[p].laz; tree[p<<1|1].laz += tree[p].laz; tree[p].laz = 0; } } void update(int p,int x,int y,int s){ if(x<=tree[p].l && y>=tree[p].r){//包含 tree[p].val+=(long long)s*(tree[p].r-tree[p].l+1); tree[p].laz+=s; return; } pushdown(p); int mid=(tree[p].l+tree[p].r)/2; if(y<=mid){ update(p<<1,x,y,s); }else if(x > mid){ update(p<<1|1,x,y,s); }else{ update(p<<1,x,mid,s); update(p<<1|1,mid+1,y,s); } tree[p].val = tree[p<<1].val + tree[p<<1|1].val; } long long query(int p,int x,int y){ if(x<=tree[p].l && y>=tree[p].r){ return tree[p].val; } pushdown(p); long long ans=0; int mid=(tree[p].l+tree[p].r)/2; if(y<=mid){ ans += query(p<<1,x,y); }else if(x > mid){ ans += query(p<<1|1,x,y); }else{ ans += query(p<<1,x,mid); ans += query(p<<1|1,mid+1,y); } return ans; } int main(){ int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&a[i]); build(1,1,n); for(int i=1;i<=m;i++) { int q,x,y,z; scanf("%d",&q); if(q==1){ scanf("%d%d%d",&x,&y,&z); update(1,x,y,z); } else { scanf("%d%d",&x,&y); cout<<query(1,x,y)<<endl; } } return 0; }
先更新到这,时间不早,下次有时间继续更新