线段树专题(持续更新中...)
单点更新:最最基础的线段树,只更新叶子节点,然后把信息用PushUP(int r)这个函数更新上来
-
线段树功能:update:单点增减 query:区间求和
-
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 6 #define LL(x) (x<<1) 7 #define RR(x) (x<<1|1) 8 #define MID(a,b) (a+((b-a)>>1)) 9 const int N=50005; 10 11 struct node 12 { 13 int lft,rht; 14 int sum; 15 int mid() 16 { 17 return MID(lft,rht); 18 } 19 }; 20 21 int y[N],n; 22 23 struct Segtree 24 { 25 node tree[N*4]; 26 void build(int lft,int rht,int rt) 27 { 28 tree[rt].lft=lft; 29 tree[rt].rht=rht; 30 tree[rt].sum=0; 31 if(lft==rht) 32 tree[rt].sum=y[lft]; 33 else 34 { 35 int mid=tree[rt].mid(); 36 build(lft,mid,LL(rt)); 37 build(mid+1,rht,RR(rt)); 38 tree[rt].sum=tree[LL(rt)].sum+tree[RR(rt)].sum; 39 } 40 } 41 void updata(int pos,int rt,int valu) 42 { 43 if(tree[rt].lft==tree[rt].rht) 44 tree[rt].sum+=valu; 45 else 46 { 47 int mid=tree[rt].mid(); 48 if(pos<=mid) 49 updata(pos,LL(rt),valu); 50 else 51 updata(pos,RR(rt),valu); 52 tree[rt].sum=tree[LL(rt)].sum+tree[RR(rt)].sum; 53 } 54 } 55 int query(int st,int ed,int rt) 56 { 57 int lft=tree[rt].lft,rht=tree[rt].rht; 58 if(st<=lft&&rht<=ed) 59 return tree[rt].sum; 60 else 61 { 62 int mid=tree[rt].mid(); 63 int sum1=0,sum2=0; 64 if(st<=mid) 65 sum1=query(st,ed,LL(rt)); 66 if(ed>mid) 67 sum2=query(st,ed,RR(rt)); 68 return sum1+sum2; 69 } 70 } 71 }seg; 72 int main() 73 { 74 int t,t_cnt=0; 75 scanf("%d",&t); 76 while(t--) 77 { 78 int a,b; 79 char str[10]; 80 scanf("%d",&n); 81 for(int i=1; i<=n; i++) 82 scanf("%d",&y[i]); 83 seg.build(1,n,1); 84 printf("Case %d:\n",++t_cnt); 85 while(1) 86 { 87 scanf("%s",str); 88 if(strcmp(str,"End")==0) 89 break; 90 scanf("%d%d",&a,&b); 91 if(strcmp(str,"Add")==0) 92 seg.updata(a,1,b); 93 else if(strcmp(str,"Sub")==0) 94 seg.updata(a,1,-b); 95 else 96 printf("%d\n",seg.query(a,b,1)); 97 } 98 } 99 return 0; 100 }
hdu1754 I Hate It
线段树功能:update:单点替换 query:区间最值,只要在上题代码的基础上稍加修改就好了,节点存储的是区间最大值
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 6 #define LL(x) (x<<1) 7 #define RR(x) (x<<1|1) 8 #define MID(a,b) (a+((b-a)>>1)) 9 const int N=200005; 10 #define INF (1<<30) 11 struct node 12 { 13 int lft,rht; 14 int sum; 15 int mid() 16 { 17 return MID(lft,rht); 18 } 19 }; 20 struct Segtree 21 { 22 node tree[N*4]; 23 void build(int lft,int rht,int rt) 24 { 25 tree[rt].lft=lft; 26 tree[rt].rht=rht; 27 tree[rt].sum=-INF; 28 if(lft==rht) 29 scanf("%d",&tree[rt].sum); 30 else 31 { 32 int mid=tree[rt].mid(); 33 build(lft,mid,LL(rt)); 34 build(mid+1,rht,RR(rt)); 35 tree[rt].sum=max(tree[LL(rt)].sum,tree[RR(rt)].sum); 36 } 37 } 38 void updata(int pos,int rt,int valu) 39 { 40 int lft=tree[rt].lft,rht=tree[rt].rht; 41 if(tree[rt].lft==tree[rt].rht) 42 tree[rt].sum=valu; 43 else 44 { 45 int mid=tree[rt].mid(); 46 if(pos<=mid) 47 updata(pos,LL(rt),valu); 48 else 49 updata(pos,RR(rt),valu); 50 tree[rt].sum=max(tree[LL(rt)].sum,tree[RR(rt)].sum); 51 } 52 } 53 int query(int st,int ed,int rt) 54 { 55 int lft=tree[rt].lft,rht=tree[rt].rht; 56 if(st<=lft&&rht<=ed) 57 return tree[rt].sum; 58 else 59 { 60 int mid=tree[rt].mid(); 61 int sum1=-INF,sum2=-INF; 62 if(st<=mid) 63 sum1=query(st,ed,LL(rt)); 64 if(ed>mid) 65 sum2=query(st,ed,RR(rt)); 66 return max(sum1,sum2); 67 } 68 } 69 } seg; 70 int main() 71 { 72 int n,m; 73 while(scanf("%d%d",&n,&m)!=EOF) 74 { 75 int a,b; 76 char str[10]; 77 seg.build(1,n,1); 78 for(int i=0; i<m; i++) 79 { 80 scanf("%s",str); 81 scanf("%d%d",&a,&b); 82 if(str[0]=='Q') 83 printf("%d\n",seg.query(a,b,1)); 84 else 85 seg.updata(a,1,b); 86 } 87 } 88 return 0; 89 }
hdu1394 Minimum Inversion Number
题意:求改变后的最小逆序数
思路:用O(nlogn)复杂度求出最初逆序数后,就可以用O(1)的复杂度分别递推出其他解
线段树功能:update:单点增减 query:区间求和
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 6 #define LL(x) (x<<1) 7 #define RR(x) (x<<1|1) 8 #define MID(a,b) (a+((b-a)>>1)) 9 const int N=5005; 10 #define INF (1<<30) 11 struct node 12 { 13 int lft,rht; 14 int sum; 15 int mid() 16 { 17 return MID(lft,rht); 18 } 19 }; 20 struct Segtree 21 { 22 node tree[N*4]; 23 void build(int lft,int rht,int rt) 24 { 25 tree[rt].lft=lft; 26 tree[rt].rht=rht; 27 tree[rt].sum=0; 28 if(lft!=rht) 29 { 30 int mid=tree[rt].mid(); 31 build(lft,mid,LL(rt)); 32 build(mid+1,rht,RR(rt)); 33 } 34 } 35 void updata(int pos,int rt) 36 { 37 int lft=tree[rt].lft,rht=tree[rt].rht; 38 if(lft==rht) 39 tree[rt].sum++; 40 else 41 { 42 int mid=tree[rt].mid(); 43 if(pos<=mid) 44 updata(pos,LL(rt)); 45 else 46 updata(pos,RR(rt)); 47 tree[rt].sum=tree[LL(rt)].sum+tree[RR(rt)].sum; 48 } 49 } 50 int query(int st,int ed,int rt) 51 { 52 int lft=tree[rt].lft,rht=tree[rt].rht; 53 if(st<=lft&&rht<=ed) 54 return tree[rt].sum; 55 else 56 { 57 int mid=tree[rt].mid(); 58 int sum1=0,sum2=0; 59 if(st<=mid) 60 sum1=query(st,ed,LL(rt)); 61 if(ed>mid) 62 sum2=query(st,ed,RR(rt)); 63 return sum1+sum2; 64 } 65 } 66 } seg; 67 int main() 68 { 69 int n; 70 int sum; 71 while(scanf("%d",&n)!=EOF) 72 { 73 sum=0; 74 int a[5001]; 75 seg.build(0,n-1,1); 76 for(int i=1; i<=n; i++) 77 { 78 scanf("%d",&a[i]); 79 seg.updata(a[i],1); 80 if(a[i]!=n-1) 81 sum+=seg.query(a[i]+1,n-1,1); 82 } 83 int minm=sum; 84 for(int i=1;i<=n;i++) 85 { 86 sum-=a[i]; 87 sum+=n-a[i]-1; 88 minm=min(minm,sum); 89 } 90 printf("%d\n",minm); 91 } 92 return 0; 93 }
hdu2795 Billboard
题意:h*w的木板,放进一些1*L的物品,求每次放空间能容纳且最上边的位子
思路:因为最多只有二十万张海报,所以板的最大的长度不会超过二十万,但是要小心,如果板的长度小于h,我们还要用h来建树。起初在查询的时候并不直接去更新它,而是查询找出它的更新位置的后,再写个updata函数去更新,但是我们可以在查询到它的位置的时候,同时去更新当前点的剩余长度,然后回溯更新所有祖先区间。返回它的查询位置的时候,因为左儿子找到的位置是pos1,右儿子找到的位置是pos2,两者都赋初值为0,又因为每次查询只找出一个位置,也就是说pos1和pos2中只有一个被改变,另一个仍保持0,所以返回pos1+pos2是正确的。
线段树功能:query:区间求最大值的位子
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 6 #define LL(x) (x<<1) 7 #define RR(x) (x<<1|1) 8 #define MID(a,b) (a+((b-a)>>1)) 9 const int N=200005; 10 #define INF (1<<30) 11 int h,w,n; 12 struct node 13 { 14 int lft,rht; 15 int sum; 16 int mid() 17 { 18 return MID(lft,rht); 19 } 20 }; 21 struct Segtree 22 { 23 node tree[N*4]; 24 void build(int lft,int rht,int rt) 25 { 26 tree[rt].lft=lft; 27 tree[rt].rht=rht; 28 tree[rt].sum=w; 29 if(lft!=rht) 30 { 31 int mid=tree[rt].mid(); 32 build(lft,mid,LL(rt)); 33 build(mid+1,rht,RR(rt)); 34 } 35 } 36 int updata(int value,int rt) 37 { 38 int pos; 39 int lft=tree[rt].lft,rht=tree[rt].rht; 40 if(lft==rht) 41 { 42 tree[rt].sum-=value; 43 return lft; 44 } 45 else 46 { 47 if(tree[LL(rt)].sum>=value) 48 pos=updata(value,LL(rt)); 49 else 50 pos=updata(value,RR(rt)); 51 tree[rt].sum=max(tree[LL(rt)].sum,tree[RR(rt)].sum); 52 return pos; 53 } 54 } 55 /*int query(int st,int ed,int rt) 56 { 57 int lft=tree[rt].lft,rht=tree[rt].rht; 58 if(st<=lft&&rht<=ed) 59 return tree[rt].sum; 60 else 61 { 62 int mid=tree[rt].mid(); 63 int sum1=0,sum2=0; 64 if(st<=mid) 65 sum1=query(st,ed,LL(rt)); 66 if(ed>mid) 67 sum2=query(st,ed,RR(rt)); 68 return sum1+sum2; 69 } 70 }*/ 71 } seg; 72 int main() 73 { 74 int p; 75 while(scanf("%d%d%d",&h,&w,&n)!=EOF) 76 { 77 h=min(h,n); 78 seg.build(1,h,1); 79 for(int i=0; i<n; i++) 80 { 81 scanf("%d",&p); 82 if(seg.tree[1].sum<p) 83 puts("-1"); 84 else 85 printf("%d\n",seg.updata(p,1)); 86 } 87 } 88 return 0; 89 }
题意:有多组测试数据,每组数据的n和d表示,有n个数,求间距大于d的最长上升序列。
思路:线段树中叶子结点表示值为i的并且以其结束的最长上升序列是多少。每次转移的时候,查询比当前值小的最大值就可以了。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 6 #define LL(x) (x<<1) 7 #define RR(x) (x<<1|1) 8 #define MID(a,b) (a+((b-a)>>1)) 9 const int N=1e5+5; 10 #define INF (1<<30) 11 int a[N],n; 12 int imx[N];//记录最大区间长度 13 struct node 14 { 15 int lft,rht; 16 int sum; 17 int mid() 18 { 19 return MID(lft,rht); 20 } 21 }; 22 struct Segtree 23 { 24 node tree[N*4]; 25 void build(int lft,int rht,int rt) 26 { 27 tree[rt].lft=lft; 28 tree[rt].rht=rht; 29 tree[rt].sum=0; 30 if(lft!=rht) 31 { 32 int mid=tree[rt].mid(); 33 build(lft,mid,LL(rt)); 34 build(mid+1,rht,RR(rt)); 35 } 36 } 37 void updata(int pos,int rt,int value) 38 { 39 int lft=tree[rt].lft,rht=tree[rt].rht; 40 if(lft==rht) 41 { 42 tree[rt].sum=max(tree[rt].sum,value); 43 } 44 else 45 { 46 int mid=tree[rt].mid(); 47 if(pos<=mid) 48 updata(pos,LL(rt),value); 49 else 50 updata(pos,RR(rt),value); 51 tree[rt].sum=max(tree[LL(rt)].sum,tree[RR(rt)].sum); 52 } 53 } 54 int query(int st,int ed,int rt) 55 { 56 int lft=tree[rt].lft,rht=tree[rt].rht; 57 if(st<=lft&&rht<=ed) 58 return tree[rt].sum; 59 else 60 { 61 int mid=tree[rt].mid(); 62 int sum1=0,sum2=0; 63 if(st<=mid) 64 sum1=query(st,ed,LL(rt)); 65 if(ed>mid) 66 sum2=query(st,ed,RR(rt)); 67 return max(sum1,sum2); 68 } 69 } 70 } seg; 71 int main() 72 { 73 int n,d; 74 while(scanf("%d%d",&n,&d)!=EOF) 75 { 76 int maxm=0; 77 int ans=0; 78 for(int i=0;i<n;i++) 79 { 80 scanf("%d",&a[i]); 81 maxm=max(maxm,a[i]); 82 imx[i]=0; 83 } 84 seg.build(0,maxm,1); 85 for(int i=0;i<n;i++) 86 { 87 if(i-d-1>=0)//间隔大于d 88 seg.updata(a[i-d-1],1,imx[i-d-1]); 89 if(a[i]>0) 90 imx[i]=seg.query(0,a[i]-1,1)+1;//每次转移的时候,查询比当前值小的最大值就可以了。 91 else 92 imx[i]=1; 93 ans=max(ans,imx[i]); 94 } 95 printf("%d\n",ans); 96 } 97 return 0; 98 }
最近做多校做到一道线段树,刚开始不会做,看了题解才会的,弱!
题意:给定n个数,m次操作,操作有两种
(1)0 a b:输出区间[a,b]中美丽序列的最大和,区间[a,b]的美丽序列:[a,b]的子序列,且相邻元素的下标奇偶性不同
(2)1 a b:将下标为a的值改为b
思路:
最大和要求子序列的相邻元素下标奇偶性不同,即下标奇偶交替即可
则共有四种情况:子序列的开始下标和结尾下标为
奇奇(jj),奇偶(jo),偶偶(oo),偶奇(oj)
jj=jo+jj,jj=jj+oj,jo=jo+jo,jo=jj+oo
oo=oo+jo,oo=oj+oo,oj=oo+oj,oj=oj+oj
根据左右孩子推父结点时需要区间合并:(合并左右孩子)
父结点的jj为 max(lson.jj,rson.jj,lson.jo+rson.jj,lson.jj+rson.oj)
同样其他三种情况类推
查询时要注意同样是区间合并(合并ans和包含要查询的子区间的结点),并不是单纯的取最大值
最后的最大和为 max(ans.jj,ans.jo,ans.oj,ans.oo);
因为题中存在负数,题中涉及到求最大值,相关的初始化要初始为负很大的数
参考代码:
1 #include<stdio.h> 2 #include<algorithm> 3 #include<string.h> 4 #define inf 0xffffffffffff 5 using namespace std; 6 #define LL(x) (x<<1) 7 #define RR(x) (x<<1|1) 8 #define MID(a,b) (a+((b-a)>>1)) 9 typedef long long LL; 10 const int N=100001; 11 int n,m; 12 LL a[N+10]; 13 struct node 14 { 15 LL jj,jo,oj,oo; 16 } tt; 17 struct Segtree 18 { 19 node tree[N*4]; 20 void pushup(node& t,node lson,node rson) 21 { 22 //更新jo 23 t.jo=max(lson.jo,rson.jo); 24 t.jo=max(t.jo,lson.jo+rson.jo); 25 t.jo=max(t.jo,lson.jj+rson.oo); 26 //更新jj 27 t.jj=max(lson.jj,rson.jj); 28 t.jj=max(t.jj,lson.jo+rson.jj); 29 t.jj=max(t.jj,lson.jj+rson.oj); 30 //更新oo 31 t.oo=max(lson.oo,rson.oo); 32 t.oo=max(t.oo,lson.oo+rson.jo); 33 t.oo=max(t.oo,lson.oj+rson.oo); 34 //更新oj 35 t.oj=max(lson.oj,rson.oj); 36 t.oj=max(t.oj,lson.oo+rson.jj); 37 t.oj=max(t.oj,lson.oj+rson.oj); 38 } 39 void build(int l,int r,int rt) 40 { 41 if(l==r) 42 { 43 tree[rt].jo=tree[rt].oj=-inf; 44 tree[rt].oo=tree[rt].jj=-inf; 45 if(l&1) 46 { 47 tree[rt].jj=a[l]; 48 } 49 else 50 { 51 tree[rt].oo=a[l]; 52 } 53 return; 54 } 55 int mid=(l+r)/2; 56 build(l,mid,LL(rt)); 57 build(mid+1,r,RR(rt)); 58 pushup(tree[rt],tree[rt<<1],tree[rt<<1|1]); 59 } 60 void updata(int l,int r,int rt,int pos,int valu) 61 { 62 if(l==r) 63 { 64 if(l&1) 65 tree[rt].jj=valu; 66 else 67 tree[rt].oo=valu; 68 return; 69 } 70 int mid=(l+r)>>1; 71 if(pos<=mid) 72 updata(l,mid,rt<<1,pos,valu); 73 else 74 updata(mid+1,r,rt<<1|1,pos,valu); 75 pushup(tree[rt],tree[rt<<1],tree[rt<<1|1]); 76 } 77 void query(int l,int r,int rt,int st,int ed) 78 { 79 if(st<=l&&r<=ed) 80 { 81 pushup(tt,tt,tree[rt]); 82 return; 83 } 84 int mid=(l+r)/2; 85 if(st<=mid) 86 query(l,mid,LL(rt),st,ed); 87 if(mid<ed) 88 query(mid+1,r,RR(rt),st,ed); 89 } 90 } seg; 91 int main() 92 { 93 int T; 94 scanf("%d",&T); 95 while(T--) 96 { 97 scanf("%d%d",&n,&m); 98 for(int i=1; i<=n; i++) 99 scanf("%I64d",&a[i]); 100 seg.build(1,n,1); 101 for(int i=0; i<m; i++) 102 { 103 int op,a,b; 104 scanf("%d%d%d",&op,&a,&b); 105 if(op) 106 { 107 seg.updata(1,n,1,a,b); 108 } 109 else 110 { 111 tt.oo=tt.oj=tt.jo=tt.jj=-inf; 112 seg.query(1,n,1,a,b); 113 long long maxm=tt.oo; 114 maxm=max(maxm,tt.oj); 115 maxm=max(maxm,tt.jj); 116 maxm=max(maxm,tt.jo); 117 printf("%I64d\n",maxm); 118 } 119 } 120 } 121 return 0; 122 }
好几天没做线段树了,回家前就练线段树了!
今天做的这道题很巧妙,只要了解做法就很好敲了。
题意:有N个人排队,每一个人都有一个val来对应,每一个后来人都会插入当前队伍的某一个位置pos。要求把队伍最后的状态输出。
思路:首先,我们想到最后那个人n的位置肯定是固定的了,所以,我们要求第n-1个人的位置就要在第n个人位置固定的基础上再找第pos[n-1]个空位。
以此类推,我们可以知道要得到最终状态,就要从后往前定位,先建树,树的每个节点存左右区间和该区间内的空位数,然后开始找位置,找到对应的位置,将该位置的空位数置为0,然后要向上更新树。。。是不是很简单~。~
参考代码:
1 #include<stdio.h> 2 #include<algorithm> 3 #include<string.h> 4 using namespace std; 5 #define LL(x) (x<<1) 6 #define RR(x) (x<<1|1) 7 #define MID(a,b) (a+((b-a)>>1)) 8 int n; 9 int pos[200005],val[200005],res[200005]; 10 struct node 11 { 12 int lft,rht,valu; 13 int mid() 14 { 15 return MID(lft,rht); 16 } 17 }; 18 struct Segtree 19 { 20 node tree[200005*4]; 21 void pushup(int rt) 22 { 23 tree[rt].valu=tree[LL(rt)].valu+tree[RR(rt)].valu; 24 } 25 void build(int l,int r,int rt) 26 { 27 tree[rt].lft=l; 28 tree[rt].rht=r; 29 tree[rt].valu=r-l+1; 30 if(l!=r) 31 { 32 int mid=tree[rt].mid(); 33 build(l,mid,LL(rt)); 34 build(mid+1,r,RR(rt)); 35 } 36 } 37 int query(int p,int rt) 38 { 39 int lft=tree[rt].lft; 40 int rht=tree[rt].rht; 41 if(lft==rht) 42 { 43 tree[rt].valu=0; 44 return lft; 45 } 46 else 47 { 48 int pos; 49 if(tree[LL(rt)].valu>=p) 50 pos=query(p,LL(rt)); 51 else 52 pos=query(p-(tree[LL(rt)].valu),RR(rt)); 53 pushup(rt); 54 return pos; 55 } 56 } 57 } seg; 58 int main() 59 { 60 while(scanf("%d",&n)!=EOF) 61 { 62 seg.build(0,n-1,1); 63 for(int i=0; i<n; i++) 64 scanf("%d%d",&pos[i],&val[i]); 65 for(int i=n-1; i>=0; i--) 66 { 67 int p=seg.query(pos[i]+1,1); 68 res[p]=val[i]; 69 } 70 for(int i=0; i<n; i++) 71 { 72 if(i) 73 { 74 printf(" %d",res[i]); 75 } 76 else 77 printf("%d",res[i]); 78 } 79 printf("\n"); 80 } 81 return 0; 82 }
题意:有N头牛,每只牛有一个测试值[S,E],如果对于牛i和牛j来说,它们的测验值满足下面的条件则证明牛i比牛j强壮:Si <= Sj and Ej <= Ei and Ei - Si > Ej - Sj。现在已知每一头牛的测验值,要求输出每头牛有几头牛比其强壮。
思路: 先将S从小到大排序,E从大到小排序,这样就可以保证在放入一个区间里,前面所以放下的区间的左端点都是小于等于它的。那么,这时候查询,有多少个区间的右端点是大于等于它的右端点的,就可以得出答案。
需要注意的是,在查询的时候,如果当前区间和前面一个区间是完全相同的话,那么直接将前面的区间得到的答案赋给它就可以,不然,查询大于等于它的右端点的数目。
如果在查询的时候,把相同的区间排除掉就可以了。
参考代码:
1 #include <set> 2 #include <map> 3 #include <list> 4 #include <queue> 5 #include <stack> 6 #include <string> 7 #include <math.h> 8 #include <time.h> 9 #include <vector> 10 #include <stdio.h> 11 #include <sstream> 12 #include <string.h> 13 #include <stdlib.h> 14 #include <iostream> 15 #include <algorithm> 16 using namespace std; 17 /***************************************/ 18 typedef vector<int> VI; 19 typedef vector<char> VC; 20 typedef vector<string> VS; 21 typedef set<int> SI; 22 typedef set<string> SS; 23 typedef map<int ,int> MII; 24 typedef map<string,int> MSI; 25 typedef pair<int,int> PII; 26 typedef vector<PII> VII; 27 typedef vector<VI > VVI; 28 /***************************************/ 29 #ifdef _WIN32 30 #define ll __int64 31 #else 32 #define ll long long 33 #endif 34 #define MID(a,b) (a+((b-a)>>1)) 35 #define mem(a,b) memset(a,b,sizeof(a)) 36 #define all(x) (x).begin(), (x).end() 37 #define sz(x) ((int)(x).size()) 38 #define PB push_back 39 #define MP make_pair 40 #define LL(x) ((x)<<1) 41 #define RR(x) ((x)<<1|1) 42 #define sqr(x) ((x)*(x)) 43 #define pn() printf("\n") 44 #define sqr(x) ((x)*(x)) 45 /***************************************/ 46 const int INF = 0x7f7f7f7f; 47 const ll LINF = (1LL<<60); 48 const double eps = 1e-8; 49 const double PIE=acos(-1.0); 50 const int dx[]= {0,-1,0,1}; 51 const int dy[]= {1,0,-1,0}; 52 const int fx[]= {-1,-1,-1,0,0,1,1,1}; 53 const int fy[]= {-1,0,1,-1,1,-1,0,1}; 54 /***************************************/ 55 void openfile() 56 { 57 freopen("data.in","rb",stdin); 58 freopen("data.out","wb",stdout); 59 } 60 void Scan(int& res) 61 { 62 int flag=0; 63 char ch; 64 while(!(((ch=getchar())>='0'&&ch<='9')||ch=='-')) 65 if(ch==EOF) 66 res=INF; 67 if(ch=='-') 68 flag=1; 69 else if(ch>='0'&&ch<='9') 70 res=ch-'0'; 71 while((ch=getchar())>='0'&&ch<='9') 72 res=res*10+ch-'0'; 73 res=flag?-res:res; 74 } 75 void Out(int a) 76 { 77 if(a>9) 78 Out(a/10); 79 putchar(a%10+'0'); 80 } 81 void Out(ll a) 82 { 83 if(a>9) 84 Out(a/10); 85 putchar(a%10+'0'); 86 } 87 /**********************The End OF The Template*****************/ 88 89 const int N=1e5+5; 90 int res[N]; 91 struct cow 92 { 93 int s,e,id; 94 } cows[N]; 95 struct node 96 { 97 int lft,rht,sum; 98 int mid() 99 { 100 return MID(lft,rht); 101 } 102 }; 103 struct segtree 104 { 105 node tree[N*4]; 106 void build(int lft,int rht,int rt) 107 { 108 tree[rt].lft=lft; 109 tree[rt].rht=rht; 110 tree[rt].sum=0; 111 if(lft!=rht) 112 { 113 int mid=tree[rt].mid(); 114 build(lft,mid,LL(rt)); 115 build(mid+1,rht,RR(rt)); 116 } 117 } 118 int query(int st,int ed,int rt) 119 { 120 int lft=tree[rt].lft; 121 int rht=tree[rt].rht; 122 if(st<=lft&&ed>=rht) 123 { 124 return tree[rt].sum; 125 } 126 else 127 { 128 int sum=0; 129 int mid=tree[rt].mid(); 130 if(st<=mid) 131 sum+=query(st,ed,LL(rt)); 132 if(ed>mid) 133 sum+=query(st,ed,RR(rt)); 134 return sum; 135 } 136 } 137 void updata(int pos,int ind) 138 { 139 tree[ind].sum++; 140 if(tree[ind].lft==tree[ind].rht) 141 return; 142 else 143 { 144 int mid=tree[ind].mid(); 145 if(pos<=mid) 146 updata(pos,LL(ind)); 147 else 148 updata(pos,RR(ind)); 149 } 150 } 151 }seg; 152 int cmp(cow x,cow y) 153 { 154 return x.s<y.s||(x.s==y.s&&x.e>y.e); 155 } 156 int main() 157 { 158 int n; 159 int st,ed; 160 while(scanf("%d",&n)!=EOF&&n) 161 { 162 for(int i=0; i<n; i++) 163 { 164 scanf("%d%d",&st,&ed); 165 cows[i].s=st; 166 cows[i].e=ed; 167 cows[i].id=i; 168 } 169 sort(cows,cows+n,cmp); 170 seg.build(0,N,1); 171 for(int i=0; i<n; i++) 172 { 173 int p=cows[i].id,q=cows[i-1].id; 174 if(i&&cows[i].s==cows[i-1].s&&cows[i].e==cows[i-1].e) 175 res[p]=res[q]; 176 else res[p]=seg.query(cows[i].e,N,1); 177 seg.updata(cows[i].e,1); 178 } 179 for(int i=0; i<n; i++) 180 { 181 if(i) 182 printf(" %d",res[i]); 183 else 184 printf("%d",res[i]); 185 } 186 printf("\n"); 187 } 188 return 0; 189 }
二、成段更新
简单的说明:成段更新需要用到延迟标记(或者说懒惰标记),简单来说就是每次更新的时候不要更新到底,用延迟标记使得更新延迟到下次需要更新or询问到的时候。延迟标记的意思是:这个区间的左右儿子都需要被更新,但是当前区间已经更新了。
poj3468 A SimpleProblem with Integers(lazy思想详解)
题意:给你N个数,Q个操作,操作有两种,‘Q a b ’是询问a~b这段数的和,‘C a b c’是把a~b这段数都加上c。
思路:
介绍Lazy思想:lazy-tag思想,记录每一个线段树节点的变化值,当这部分线段的一致性被破坏我们就将这个变化值传递给子区间,大大增加了线段树的效率。
在此通俗的解释我理解的Lazy意思,比如现在需要对[a,b]区间值进行加c操作,那么就从根节点[1,n]开始调用update函数进行操作,如果刚好执行到一个子节点,它的节点标记为rt,这时tree[rt].l == a && tree[rt].r == b 这时我们可以一步更新此时rt节点的sum[rt]的值,sum[rt] += c * (tree[rt].r - tree[rt].l + 1),注意关键的时刻来了,如果此时按照常规的线段树的update操作,这时候还应该更新rt子节点的sum[]值,而Lazy思想恰恰是暂时不更新rt子节点的sum[]值,到此就return,直到下次需要用到rt子节点的值的时候才去更新,这样避免许多可能无用的操作,从而节省时间 。
下面通过具体的代码来说明之,在此先介绍下代码中的函数说明:
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
宏定义左儿子lson和右儿子rson,貌似用宏的速度要慢。
PushUp(rt):通过当前节点rt把值递归向上更新到根节点
PushDown(rt):通过当前节点rt递归向下去更新rt子节点的值
rt表示当前子树的根(root),也就是当前所在的结点
下面直接来介绍update函数,Lazy操作主要就是用在这里:
void update(int c,int l,int r,int rt)//表示对区间[l,r]内的每个数均加c,rt是根节点 { if(tree[rt].l == l && r == tree[rt].r) { add[rt] += c; sum[rt] += (__int64)c * (r-l+1); return; } if(tree[rt].l == tree[rt].r) return; PushDown(rt,tree[rt].r - tree[rt].l + 1); int m = tree[rt].mid(); if(r <= m) update(c,l,r,rt<<1); else if(l > m) update(c,l,r,rt<<1|1); else { update(c,l,m,rt<<1); update(c,m+1,r,rt<<1|1); } PushUp(rt); }
if(tree[rt].l == l && r == tree[rt].r) 这里就是用到Lazy思想的关键时刻 正如上面说提到的,这里首先更新该节点的sum[rt]值,然后更新该节点具体每个数值应该加多少即add[rt]的值,注意此时整个函数就运行完了,直接return,而不是还继续向子节点继续更新,这里就是Lazy思想,暂时不更新子节点的值。
那么什么时候需要更新子节点的值呢?答案是在某部分update操作的时候需要用到那部分没有更新的节点的值的时候,这里可能有点绕口。这时就掉用PushDown()函数更新子节点的数值。
void PushDown(int rt,int m) { if(add[rt]) { add[rt<<1] += add[rt]; add[rt<<1|1] += add[rt]; sum[rt<<1] += add[rt] * (m - (m>>1)); sum[rt<<1|1] += add[rt] * (m>>1); add[rt] = 0;//更新后需要还原 } }
PushDown就是从当前根节点rt向下更新每个子节点的值,这段代码读者可以自己好好理解,这也是Lazy的关键。
下面再解释query函数,也就是用这个函数来求区间和
__int64 query(int l,int r,int rt) { if(l == tree[rt].l && r == tree[rt].r) { return sum[rt]; } PushDown(rt,tree[rt].r - tree[rt].l + 1); int m = tree[rt].mid(); __int64 res = 0; if(r <= m) res += query(l,r,rt<<1); else if(l > m) res += query(l,r,rt<<1|1); else { res += query(l,m,rt<<1); res += query(m+1,r,rt<<1|1); } return res; }
第一个if还是区间的判断和前面update的一样,到这里就可以知道答案了,所以就直接return。
接下来的查询就需要用到rt子节点的值了,由于我们用了Lazy操作,这段的数值还没有更新,因此我们需要调用PushDown函数去更新之,满足if(add[rt])就说明还没有更新。
参考代码:
1 #include <set> 2 #include <map> 3 #include <list> 4 #include <queue> 5 #include <stack> 6 #include <string> 7 #include <math.h> 8 #include <time.h> 9 #include <vector> 10 #include <stdio.h> 11 #include <sstream> 12 #include <string.h> 13 #include <stdlib.h> 14 #include <iostream> 15 #include <algorithm> 16 using namespace std; 17 #define LL(x) (x<<1) 18 #define RR(x) (x<<1|1) 19 #define MID(a,b) (a+((b-a)>>1)) 20 const int N = 1e5 + 5; 21 struct cow 22 { 23 int s, e, id; 24 } cows[N]; 25 struct node 26 { 27 int lft, rht; 28 long long sum,add; 29 int mid() 30 { 31 return MID(lft, rht); 32 } 33 }; 34 struct segtree 35 { 36 node tree[N * 4]; 37 void pushup(int rt) 38 { 39 tree[rt].sum = tree[LL(rt)].sum + tree[RR(rt)].sum; 40 } 41 void pushdown(int rt, int m) 42 { 43 if (tree[rt].add) 44 { 45 tree[LL(rt)].add +=tree[rt].add; 46 tree[RR(rt)].add += tree[rt].add; 47 tree[LL(rt)].sum += tree[rt].add*(m-(m>>1)); 48 tree[RR(rt)].sum += tree[rt].add*(m >> 1); 49 tree[rt].add = 0; 50 } 51 } 52 void build(int lft, int rht, int rt) 53 { 54 tree[rt].lft = lft; 55 tree[rt].rht = rht; 56 tree[rt].sum = tree[rt].add = 0; 57 if (lft == rht){ 58 scanf("%I64d", &tree[rt].sum); 59 return; 60 } 61 else 62 { 63 int mid = tree[rt].mid(); 64 build(lft, mid, LL(rt)); 65 build(mid + 1, rht, RR(rt)); 66 pushup(rt); 67 } 68 } 69 long long query(int st, int ed, int rt) 70 { 71 int lft = tree[rt].lft; 72 int rht = tree[rt].rht; 73 if (st == lft && ed == rht) 74 { 75 return tree[rt].sum; 76 } 77 pushdown(rt,rht-lft + 1); 78 int mid = tree[rt].mid(); 79 long long res = 0; 80 if (ed<= mid) 81 res += query(st, ed, LL(rt)); 82 else if (st > mid) 83 res += query(st, ed, RR(rt)); 84 else 85 { 86 res += query(st, mid, LL(rt)); 87 res += query(mid + 1, ed, RR(rt)); 88 } 89 return res; 90 } 91 void update(int st, int ed, int rt, int add) 92 { 93 int lft = tree[rt].lft, rht = tree[rt].rht; 94 if (st == lft && ed == rht) 95 { 96 tree[rt].add += add; 97 tree[rt].sum += (long long)(rht - lft + 1) * add; 98 return; 99 } 100 if (tree[rt].lft == tree[rt].rht) 101 return; 102 pushdown(rt, rht - lft + 1); 103 int mid = tree[rt].mid(); 104 if (ed <= mid) 105 update(st, ed, LL(rt), add); 106 else if (st > mid) 107 update(st, ed, RR(rt), add); 108 else 109 { 110 update(st, mid, LL(rt), add); 111 update(mid + 1, ed, RR(rt), add); 112 } 113 pushup(rt); 114 } 115 } seg; 116 int main() 117 { 118 int n, m; 119 scanf("%d%d", &n, &m); 120 seg.build(1, n, 1); 121 for (int i = 0; i < m; i++) 122 { 123 char op[2]; 124 scanf("%s", op); 125 int a, b, c; 126 if (op[0] == 'Q') 127 { 128 scanf("%d%d", &a, &b); 129 printf("%I64d\n", seg.query(a, b, 1)); 130 } 131 else 132 { 133 scanf("%d%d%d", &a, &b, &c); 134 seg.update(a, b, 1, c); 135 } 136 } 137 return 0; 138 }
题意:给你T组数据,N个数(初始时每个数的值为1),M个操作,每个操作把区间[a,b]里的数更新为c,问最后这N个数的和是多少。
思路:比上题简单,在上题代码上稍加修改即可。
参考代码:
1 #include <set> 2 #include <map> 3 #include <list> 4 #include <queue> 5 #include <stack> 6 #include <string> 7 #include <math.h> 8 #include <time.h> 9 #include <vector> 10 #include <stdio.h> 11 #include <sstream> 12 #include <string.h> 13 #include <stdlib.h> 14 #include <iostream> 15 #include <algorithm> 16 using namespace std; 17 #define LL(x) (x<<1) 18 #define RR(x) (x<<1|1) 19 #define MID(a,b) (a+((b-a)>>1)) 20 const int N = 1e5 + 5; 21 struct cow 22 { 23 int s, e, id; 24 } cows[N]; 25 struct node 26 { 27 int lft, rht; 28 long long sum, add; 29 int mid() 30 { 31 return MID(lft, rht); 32 } 33 }; 34 struct segtree 35 { 36 node tree[N * 4]; 37 void pushup(int rt) 38 { 39 tree[rt].sum = tree[LL(rt)].sum + tree[RR(rt)].sum; 40 } 41 void pushdown(int rt, int m) 42 { 43 if (tree[rt].add) 44 { 45 tree[LL(rt)].add = tree[rt].add; 46 tree[RR(rt)].add = tree[rt].add; 47 tree[LL(rt)].sum = tree[rt].add * (m - (m >> 1)); 48 tree[RR(rt)].sum = tree[rt].add * (m >> 1); 49 tree[rt].add = 0; 50 } 51 } 52 void build(int lft, int rht, int rt) 53 { 54 tree[rt].lft = lft; 55 tree[rt].rht = rht; 56 tree[rt].add = 0; 57 if (lft == rht) 58 tree[rt].sum = 1; 59 else { 60 int mid = tree[rt].mid(); 61 build(lft, mid, LL(rt)); 62 build(mid + 1, rht, RR(rt)); 63 pushup(rt); 64 } 65 } 66 /*long long query(int st, int ed, int rt) 67 { 68 int lft = tree[rt].lft; 69 int rht = tree[rt].rht; 70 if (st == lft && ed == rht) 71 { 72 return tree[rt].sum; 73 } 74 pushdown(rt, rht - lft + 1); 75 int mid = tree[rt].mid(); 76 long long res = 0; 77 if (ed <= mid) 78 res += query(st, ed, LL(rt)); 79 else if (st > mid) 80 res += query(st, ed, RR(rt)); 81 else 82 { 83 res += query(st, mid, LL(rt)); 84 res += query(mid + 1, ed, RR(rt)); 85 } 86 return res; 87 }*/ 88 void update(int st, int ed, int rt, int c) 89 { 90 int lft = tree[rt].lft, rht = tree[rt].rht; 91 if (st == lft && ed == rht) 92 { 93 tree[rt].add= c; 94 tree[rt].sum = (long long)(rht - lft + 1) *c; 95 return; 96 } 97 if (tree[rt].lft == tree[rt].rht) 98 return; 99 pushdown(rt, rht - lft + 1); 100 int mid = tree[rt].mid(); 101 if (ed <= mid) 102 update(st, ed, LL(rt), c); 103 else if (st > mid) 104 update(st, ed, RR(rt), c); 105 else 106 { 107 update(st, mid, LL(rt), c); 108 update(mid + 1, ed, RR(rt), c); 109 } 110 pushup(rt); 111 } 112 } seg; 113 int main() 114 { 115 int T; 116 int cas=0; 117 scanf("%d", &T); 118 while (T--) 119 { 120 cas++; 121 int n, m; 122 scanf("%d%d", &n, &m); 123 seg.build(1, n, 1); 124 while (m--) 125 { 126 int a, b, c; 127 scanf("%d%d%d", &a, &b, &c); 128 seg.update(a, b, 1, c); 129 } 130 printf("Case %d: The total value of the hook is %I64d.\n",cas,seg.tree[1].sum); 131 } 132 return 0; 133 }