线段树可以分为四个部分 :单点更新 ,成段更新 ,区间合并 ,扫描线。
单点更新:即只更新叶子节点,可以单点用PushUP(int rt )来更新。
题目:
hdu1166 敌兵布阵 update 单点增加 query 区间求和
/*输入 t样例 N(N<=50000) 第i个正整数ai代表第i个工兵营地里开始时有ai个人(1<=ai<=50)。 接下来每行有一条命令,命令有4种形式: (1) Add i j,i和j为正整数,表示第i个营地增加j个人(j不超过30) (2)Sub i j ,i和j为正整数,表示第i个营地减少j个人(j不超过30); (3)Query i j ,i和j为正整数,i<=j,表示询问第i到第j个营地的总人数; (4)End 表示结束,这条命令在每组数据最后出现; 10 2 3 4 5 6 7 8 9 10 Query 1 3 Add 3 6 Query 2 7 Sub 10 2 Add 6 3 Query 3 10 End 输出 Case 1: 33 */ #include <iostream> #include <string> using namespace std; //线段树模板(单点更新) #define Mid ((l+r)>>1) // l+r/2; #define lson rt<<1,l,Mid //2*rt,l,mid, #define rson rt<<1|1,Mid+1,r //2*rt+1 ,mid+1,r; const int maxn = 100010; int sum[maxn<<2]; void build(int rt,int l,int r) { if(l==r) { scanf("%d",&sum[rt]); } else { build(lson); build(rson); sum[rt] = sum[rt<<1] + sum[rt<<1|1]; } } void update(int rt,int l,int r,int pos,int num,string c) //修改pos位 值为num { if(l == r && r == pos){ if(c == "Add") sum[rt] += num; if(c == "Sub") sum[rt] -= num; } else{ if( pos <= Mid) update(lson,pos,num,c); else update(rson,pos,num,c); sum[rt] = sum[rt<<1] + sum[rt<<1|1]; } } int query(int rt,int l,int r,int L,int R) //查询[L,R] { if(L <= l && r <= R){ return sum[rt]; } else{ int tmp = 0; if( L <= Mid) tmp += query(lson,L,R); if( R > Mid) tmp += query(rson,L,R); return tmp; } } int main() { int t,n,g=0,a,b; string c; cin>>t; while(t--) { g+=1;//case:g cin>>n; build(1,1,n); cout<<"Case "<<g<<":"<<endl; while(cin>>c) { if(c=="End") break; cin>>a>>b; if(c=="Sub") update(1,1,n,a,b, "Sub"); if(c=="Add") update(1,1,n,a,b, "Add"); if(c=="Query") cout<<query(1,1,n,a,b)<<endl; } } }
hdu1754 I Hate It update 单点替换 query 区间最值
#include <iostream> #include <string> #include <cstdio> #include <algorithm> using namespace std; //线段树模板(单点更新) #define Mid ((l+r)>>1) // l+r/2; #define lson rt<<1,l,Mid //2*rt,l,mid, #define rson rt<<1|1,Mid+1,r //2*rt+1 ,mid+1,r; const int maxn = 222222; int sum[maxn<<2]; //PushUp函数更新节点信息 ,这里是最值 。 void PushUp(int rt){ sum[rt] = max(sum[rt<<1], sum[ rt<<1|1]); } void build(int rt,int l,int r) { if(l==r) { scanf("%d",&sum[rt]); return ; } else { build(lson); build(rson); PushUp(rt); } } void update(int rt,int l,int r,int pos,int num) //修改pos位 值为num { if(l == r && r == pos){ sum[rt] = num; } else{ if( pos <= Mid) update(lson,pos,num); else update(rson,pos,num); PushUp(rt); } } int query(int rt,int l,int r,int L,int R) //查询[L,R] { if(L <= l && r <= R) { return sum[rt]; } int ret =0; if( L <= Mid) ret= max(ret,query(lson,L,R)); if( R > Mid) ret= max(ret,query(rson,L,R)); return ret; } int main() { int n,m; while(~(scanf("%d%d",&n,&m))) { build(1,1,n); while(m--) { char c; int a,b; scanf(" %c%d%d",&c,&a,&b); if(c=='Q') printf("%d ",query(1,1,n,a,b)); else update(1,1,n,a,b); } } return 0; }
成段更新:
用延迟标记 ,使得更新延迟到下次需要更新or询问的时候。
题目 :
hdu 1698 Just a Hook update成段替换 query一次总区间 可以直接输出 结点的信息。
#include <cstdio> #include <cmath> #include <iostream> #include <algorithm> using namespace std; #define Mid ((l+r)>>1) #define lson rt<<1,l,Mid #define rson rt<<1|1,Mid+1,r const int maxn = 100010; int sum[maxn<<2],add[maxn<<2]; void build(int rt,int l,int r) { add[rt] = 0; if(l == r){ sum[rt]=1; }else{ build(lson); build(rson); sum[rt]=sum[rt<<1]+sum[rt<<1|1]; } } void pushDown(int rt,int len) { add[rt<<1] = add[rt<<1|1] = add[rt]; sum[rt<<1] = (len-(len>>1))*add[rt]; sum[rt<<1|1] = (len>>1)*add[rt]; add[rt] = 0; } void update(int rt,int l,int r,int L,int R,int z) //更新[L,R]为z { if(L <= l && r <= R){ add[rt] = z; sum[rt] = (r-l+1)*z; }else{ if(add[rt]) pushDown(rt,r-l+1); if(L <= Mid) update(lson,L,R,z); if(R > Mid) update(rson,L,R,z); sum[rt] = sum[rt<<1] + sum[rt<<1|1]; } } int main() { int t,cnt=1; scanf("%d",&t); while(t--) { int n,q; scanf("%d%d",&n,&q); build(1,1,n); while(q--) { int x,y,z; scanf("%d%d%d",&x,&y,&z); update(1,1,n,x,y,z); } printf("Case %d: The total value of the hook is %d. ", cnt++,sum[1]); } return 0; }
poj 3468 A Simple Problem with Integers update 成段增减 query 区间求和。(待完善)
#include <stdio.h> #define Mid ((l+r)>>1) #define lson rt<<1,l,Mid #define rson rt<<1|1,Mid+1,r const int maxn = 100010; int sum[maxn<<2],add[maxn<<2]; void build(int rt,int l,int r) { add[rt] = 0; if(l == r){ scanf("%d",&sum[rt]); }else{ build(lson); build(rson); sum[rt]=sum[rt<<1]+sum[rt<<1|1]; } } void pushDown(int rt,int len) { add[rt<<1] += add[rt<<1|1] = add[rt]; sum[rt<<1] += (len-(len>>1))*add[rt]; sum[rt<<1|1]+= (len>>1)*add[rt]; add[rt] = 0; } void update(int rt,int l,int r,int L,int R,int z) //更新[L,R]增加z { if(L <= l && r <= R){ add[rt] += z; sum[rt] +=(r-l+1)*z; }else{ if(add[rt]) pushDown(rt,r-l+1); if(L <= Mid) update(lson,L,R,z); if(R > Mid) update(rson,L,R,z); sum[rt] = sum[rt<<1] + sum[rt<<1|1]; } } int query(int rt,int l,int r,int L,int R) //查询[L,R],调用:(1,1,n,L,R) { if(L <= l && r <= R){ return sum[rt]; } else { if(add[rt]) pushDown(rt,r-l+1); int t = 0; if(L <= Mid) t += query(lson,L,R); if(R > Mid) t += query(rson,L,R); return t; } } int main() { int n,q; while(~scanf("%d%d",&n,&q)) { build(1,1,n); while(q--) { char s; int a,b,c; scanf("%c",&s); if(s=='Q') { scanf("%d%d",&a,&b); int ans=query(1,1,n,a,b); printf("%d ",ans); } else if(s=='C') { scanf("%d%d%d",&a,&b,&c); update(1,1,n,a,b,c); } } } return 0; }
poj 2528 Mayor's posters 线段树区间更新+离散化(待更新)
/* 639.线段树-贴海报 (10分) C时间限制:3000 毫秒 | C内存限制:3000 Kb 题目内容: 由10^7块1x1的玻璃构成1x10^7的海报墙,每个海报完整地覆盖几块玻璃,海报的宽度可以不同。后来的人可以覆盖 前人的海报。一张海报如果有没被覆盖的部分,则称为可视海报。你的任务是找出有多少可视海报。 输入描述 第一行是测试的总数c,接下来的行是各测试用例。 每个测试的第一行是海报的总数n, n<=10000, 然后是n个按先后顺序贴的海报的位置li, ri. 满足1<=li<=ri<=10^7。 输出描述 每个测试的可视海报数目 输入样例 1 5 1 4 2 6 8 10 3 4 7 10 输出样例 4 */ #include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> using namespace std; #define N 11111 #define lson l, m, rt<<1 #define rson m+1, r, rt<<1|1 #define abbreviations int l,int r,int rt #define mid (l+r)>>1 int le[N],ri[N],root[N<<4],lr[N<<4],m,cnt; bool flag[N<<4]; void update(abbreviations, int L,int R,int c) { if(L <= l && R >= r) { root[rt] = c; return ; } if(root[rt] != -1) { root[rt<<1] = root[rt<<1|1] = root[rt]; root[rt] = -1; } int m = mid; if(m >= L) update(lson,L,R,c); if(m < R) update(rson,L,R,c); } void query(abbreviations) { if(root[rt] != -1) { if(!flag[root[rt]]) cnt++; flag[root[rt]] = true; return ; } if(l == r) return ; int m = mid; query(lson); query(rson); } int main() { int t,n,i,nn; scanf("%d",&t); while(t--) { scanf("%d",&n); for(i = 0,nn = 0; i < n; i++) { scanf("%d%d",&le[i],&ri[i]); lr[nn++] = le[i]; //记录左端点 lr[nn++] = ri[i]; //记录右端点 } sort(lr,lr+nn); //排序不能少 m = unique(lr,lr+nn)-lr; //去重 for(i = m-1; i >= 0; i--) { if(lr[i] != (lr[i-1]+1)) lr[m++] = lr[i] + 1; //相邻数字大于1就添加一个数 } sort(lr,lr+m); //添加数字后不要忘记排序 memset(root,-1,sizeof(root)); //建树叶子节点全部赋值为-1 for(i = 0; i < n; i++) { int L = lower_bound(lr, lr+m, le[i]) - lr; //二分查找左端点 int R = lower_bound(lr, lr+m, ri[i]) - lr; //二分查找右端点 update(0, m, 1, L, R, i); //更新 } memset(flag,false,sizeof(flag)); cnt = 0; query(0, m, 1); //求出答案 printf("%d ",cnt); } return 0; }
区间合并 :
询问满足条件的最大区间。 pushup对左右子树合并。
其它 :有的题目应用线段树需要一些排序 然后扫一遍 矩形面积的并 和 周长并。
线段树与单调栈结合:2019牛客暑期多校训练营(第四场)sequence