简单线段树
Posted cloud-king
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了简单线段树相关的知识,希望对你有一定的参考价值。
一、单点更新
hdu1166区间和
#include <iostream> #include <algorithm> #include <cmath> using namespace std; const int maxn=50005; const int maxnnode=1<<19; struct node{ int value; int left,right; }node[maxnnode]; int father[maxn]; void BuidTree(int i,int l,int r) { node[i].left=l; node[i].right=r; node[i].value=0; if(l==r) { father[l]=l; return ; } BuidTree(i<<1,l,(int)floor((l+r)/2.0)); BuidTree((i<<1)+1,(int)floor((l+r)/2.0+1),r); } void UpdataTree(int ri,int x) { if(ri==1)return; int fi=ri/2; node[fi].value+=x; UpdataTree(fi,x); } int res=0; void Query(int i,int l,int r) { if(node[i].left==l&&node[i].right==r) { res+=node[i].value; return; } i<<1; if(l<=node[i].right) { if(r<=node[i].right) Query(i,l,r); else Query(i,l,node[i].right); } i++; if(r<=node[i].left) { if(l<=node[i].left) Query(i,l,r); else Query(i,node[i].left,r); } } int main() { int t; cin >> t; while(t--){ int n,tmp,k=1; cin >> n; BuidTree(1,1,n); for(int i=0;i<n;i++) { cin >> tmp; node[father[tmp]].value=tmp; UpdataTree(father[tmp],tmp); } cout << "Case " << k << ":" << endl; while(1) { string op; int a,b; cin >> op >> a >> b; if(op[0]==‘Q‘) Query(1,a,b); else if(op[0]==‘S‘) UpdataTree(father[a],-b); else if(op[0]==‘A‘) UpdataTree(father[a],b); else break; } k++; } return 0; }
二、区间更新
POJ3468区间和,Lazy标记只有当操作到该节点时才将标价下放;
#include <iostream> #include <cstdio> using namespace std; typedef long long ll; const int maxn=1e5+10; #define lson l,m,i<<1 #define rson m+1,r,i<<1|1 ll sum[maxn<<2],add[maxn<<2]; //区间和,区间结点增加值; struct Node{ int l,r; int mid(){ return (l+r)>>1; } }tree[maxn<<2]; void PushUp(int rt) { sum[rt]=sum[rt<<1]+sum[rt<<1|1]; } 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; } } void BuidTree(int l,int r,int i) //建树; { tree[i].l=l; tree[i].r=r; add[i]=0; if(l==r){ scanf("%I64d",&sum[i]); return; } int m=tree[i].mid(); BuidTree(lson); BuidTree(rson); PushUp(i); } void UpdataTree(int c,int l,int r,int rt) { if(tree[rt].l==l&&tree[rt].r==r){ //符合查询区间条件; add[rt]+=c; sum[rt]+=(ll)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)UpdataTree(c,l,r,rt<<1); //递归向下查找匹配区间; else if(l>m) UpdataTree(c,l,r,rt<<1|1); else{ UpdataTree(c,l,m,rt<<1); UpdataTree(c,m+1,r,rt<<1|1); } PushUp(rt); } ll 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(); ll 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; } int main() { int n,m; while(~scanf("%d%d",&n,&m)){ BuidTree(1,n,1); while(m--){ char ch[3]; int a,b,c; scanf("%s",ch); //cout << ch << endl; if(ch[0]==‘Q‘) { scanf(" %d %d",&a,&b,&c); printf("%lld ",Query(a,b,1)); }else{ scanf(" %d %d %d ",&a,&b,&c); UpdataTree(c,a,b,1); } } } return 0; }
三、线段树+离散化
poj2528
#include <cstdio> #include <algorithm> #include <iostream> using namespace std; const int maxn=20005; const int MAX=1e7+5; #define lson left,m,rt<<1 #define rson m+1,right,rt<<1|1 //海报从后往前贴,避免后面的海报的干扰; int pl[maxn],pr[maxn]; //存区间端点 int port[maxn],id[MAX]; //离散化端点 bool covered[maxn<<2]; //存线段树的区间信息 struct node{ int l,r; int mid(){ return (l+r)>>1; } }tree[maxn<<2]; void BuidTree(int left,int right,int rt) { tree[rt].l=left; tree[rt].r=right; covered[rt]=false; if(left>=right) return ; int m=tree[rt].mid(); BuidTree(lson); BuidTree(rson); } bool query(int left,int right,int rt) { if(covered[rt])return false; if(left==tree[rt].l&&right==tree[rt].r){ covered[rt]=true; return true; } int m=tree[rt].mid(); bool res; if(right<=m){ res=query(left,right,rt<<1); }else if(left>m){ res=query(left,right,rt<<1|1); }else{ bool res1=query(left,m,rt<<1); bool res2=query(m+1,right,rt<<1|1); res=res1||res2; } if(covered[rt<<1]&&covered[rt<<1|1]) covered[rt]=true; return res; } int main() { int t,n; scanf("%d",&t); while(t--){ int cnt=0,ans=0; scanf("%d",&n); for(int i=0;i<n;i++) { scanf("%d%d",&pl[i],&pr[i]); port[cnt++]=pl[i]; port[cnt++]=pr[i]; } sort(port,port+cnt); cnt=unique(port,port+cnt)-port; //离散化; for(int i=0;i<cnt;i++) id[port[i]]=i+1; BuidTree(1,cnt,1); for(int i=n-1;i>=0;i--){ //cout << i <<endl; if(query(id[pl[i]],id[pr[i]],1)){ ans++; //cout << ans << endl; } } cout <<ans << endl; } return 0; }
离散化:有些数据本身很大, 自身无法作为数组的下标保存对应的属性。如果这时只是需要这堆数据的相对属性, 那么可以对其进行离散化处理。当数据只与它们之间的相对大小有关,而与具体是多少无关时,可以进行离散化。比如当你数据个数n很小,数据范围却很大时(超过1e9)就考虑离散化更小的值,能够实现更多的算法。
//1.用数组离散 for(int i=1;i<=n;i++){ cin>>a[i].val; a[i].id = i; } sort(a+1,a+1+n); for(int i=1;i<=n;i++) b[a[i].id] = i; //将a[i]数组映射成更小的值,b[i]就是a[i]对应的rank值 //2.用STL+二分离散化 for(int i=1;i<=n;i++){ cin>>a[i]; b[i] = a[i]; } sort(b+1,b+1+n); int len = unique(b+1,b+1+n)-b-1; //len就是去重之后的数组长度,unique用法可以去网上看看,用法简单 for(int i=1;i<=n;i++) a[i] = lower_bound(b+1,b+1+n,a[i])-b; //a[i]就是直接离散化出来的数组
以上是关于简单线段树的主要内容,如果未能解决你的问题,请参考以下文章