区间有关的问题,如记录一个区间的最值和总量,并在区间的插入,删除修改中维护这些最值和总量,用线段树是很方便的。线段树拥有良好的树形二分特性。
详细的概念参考百度百科http://baike.baidu.com/view/670683.htm
在实现中,用以下几个变量就足够了。n记录一共用到了多少个节点,B表示每个顶点的线段的起点,E[i]表示每个线段的终点,C是权值,表示某个线段上的权。初始值为0。用lson和rson分别表示左孩子和右孩子
实现一个线段树,代码如下
#ifndef SegmentTree_H #define SegmentTree_H class SegmentTree { public: SegmentTree(int maxsize)//参量表示线段树最多有多少个顶点 { n=0; B=new int[maxsize]; E=new int[maxsize]; c=new int[maxsize]; lson=new int[maxsize]; rson=new int[maxsize]; }; ~SegmentTree() { delete [] B; delete [] E; delete [] c; delete [] lson; delete [] rson; } void build(int a,int b); void insert(int c,int d,int v); void del(int c,int d,int v); private: int n;// to record how many nodes has been used int *B;//the begin point of node i int *E;// the end point of node i int *c;// to record the weight of node i int *lson;//the left son of node i int *rson;// the right son of node i }; void SegmentTree::build(int a, int b) { n=n+1; int v=n; B[v]=a; E[v]=b; c[v]=0; if(b-a>1) { int average=(a+b)/2; lson[v]=n+1; build(a,average); rson[v]=n+1; build(average,b); } } void SegmentTree::insert(int c,ind d,int v) { if(c<=B[v]&&d>=E[v]) { c[v]++; } else { int avg=(B[v]+E[v])/2; if(c<=avg) insert(c,d,lson[v]); if(d>avg) insert(c,d,rson[v]); } } void SegmentTree::del(int c, int d,int v) { if(c<=B[v]&&d>=E[v]) { if(c[v]>0) c[v]++; } else { int avg=(B[v]+E[v])/2; if(c<=avg) del(c,d,lson[v]); if(d>avg) del(c,d,rson[v]); } } #endif
线段树问题
1,铁路,假设城市铁路联网顺次连接着c个城市,每两个城市之间都有火车,座位数量有上限。铁路上的城市数量<=60000,座位数量<=60000,请求数量<=60000。
请求为,订票请求:如果这两个城市间的座位数量大于等于请求的数量,则输出T,并更新这两个城市间的火车的座位数量,否则,输出F。
考虑一下:如果顺次三个城市a,b,c (a,b)=3,(b,c)=2,则(a,c)=2,如果有人预定了ac之间的座位,则ab,bc的座位数量都要相应的减少,否则,预定ab的时候就可能出现错误。
该问题用线段树很容易解决。
#ifndef TRAINTICKET_H #define TRAINTICKET_H #include <iostream> class TrainTicket { public: TrainTicket() { n=0; std::cin>>numOfStation>>numOfSeats>>numOfOrders; lson=new int[2*numOfStation]; rson=new int[2*numOfStation]; c=new int[2*numOfStation]; b=new int[2*numOfStation]; e=new int[2*numOfStation]; for(int i=0;i<2*numOfStation;i++) { c[i]=numOfSeats; } build(1,numOfStation); int x,y,z; for(int i=1;i<=numOfOrders;i++) { std::cin>>x>>y>>z; if(check(x,y,1)>=z) { std::cout<<"T"<<std::endl; update(x,y,1,z); } else std::cout<<"F"<<std::endl; } } void build(int x,int y); int check(int x,int y,int v); void update(int x,int y,int v,int z); ~TrainTicket() { delete [] lson; delete [] rson; delete [] c; delete [] b; delete [] e; } private: int numOfStation;//总站数 int numOfSeats;//座位数量 int numOfOrders;//命令数 int n;//线段树用到的顶点数 int *lson; int *rson; int *c;//当前节点票数; int *b;//该节点起始点 int *e;//顶点终止点 }; void TrainTicket::build(int x, int y) { n=n+1; int v=n; b[v]=x; e[v]=y; if((y-x)>1) { lson[v]=n+1; build(x,(x+y)/2); rson[v]=n+1; build((x+y)/2,y); } } int TrainTicket::check(int x, int y,int v) { if(x>e[v]||y<b[v]) return numOfSeats; if(x<=b[v]&&y>=e[v]) return c[v]; if(e[v]-b[v]==1) return this->numOfSeats; int ave=(b[v]+e[v])/2; int left,right; left=check(x,y,lson[v]); right=check(x,y,rson[v]); return left<right?left:right; } void TrainTicket::update(int x, int y, int v, int z) { if(y<b[v]||x>e[v]) system("exit"); if((e[v]-b[v])==1) c[v]=c[v]-z; else { int ave=(b[v]+e[v])/2; if(x<=ave) update(x,y,lson[v],z); if(y>ave) update(x,y,rson[v],z); c[v]=c[lson[v]]<c[rson[v]]?c[lson[v]]:c[rson[v]]; } } #endif
2, 树的构造
一棵含有n个节点的树,所有的节点的编号为1,2,3,。。。,n,每个节点v都有一个权值s(v),也是1,2,3,4,。。n。对于编号为v的节点,定义t(v)为v的后代中所有权值小于s(v)的结点个数。现在给出这棵树及t(1),t(2),t(3),。。求出这棵树每个节点的权值。(多个解,只要任一个解就可以)
分析:不妨设有七个节点,编号为1,2,3,4,5,6,7
结构如下
1
2 3
4 5 6 7
t: 1,2,1,0,0,0,0
用先序遍历的顺序为1,2,4,5,3,6,7
设一个权值数组ans[1~7]
将Node1放到2位置,前面留空位一个,Node2放到位置4,前面留了1,3两个空位,2已经被用了
4,5的t值都为0,所以分别放到,1,3
Node3的t值为1放到6位置,前面留空位5,1~4已经被用了
则1~7的权值分配为
4 1 5 2 6 3 7
这样一来,只需要一次深度优先遍历就可以了,然后在权值数组上采用如上方法查找空位填入数字即可。最后按照节点顺序输出权值,就分配好了。这样一来代码比较简单
但是分配权值的这个可以使用线段树来实现。这样代码就比较复杂,而且实现过程和上面也略有不同,解也不同
锻炼一下线段树的实用,代码如下
#ifndef TREEBUILD_H #define TREEBUILD_H class TreeBuild { int maxNumOfNodes; int *left;// 线段树位置左指针 int *right;//线段树位置右指针 int *m;//线段树权值 int **g;//表示第i个节点的第j个儿子的序号 int *ans;//权值表 int *b;//度数表 int *q;//先序遍历序列 bool *vis;//是否访问的标示符 int n;//顶点个数 int *t;//顶点i的所有后代中,权值小于ans[i]的顶点个数 public: TreeBuild(); void dfs(int x,int index);//x为深度优先搜索中第index个 void build(int p,int l,int r); int max(int x,int y); void insert(int p,int l,int r,int k,int w);//如果顶点p对应的区间空位数为k,在节点p,区间l~r上的第k个空位插入权重为w的点 void find(int p,int l,int r, int k,int w);//如果顶点p对应的区间空位数大于等于k,在顶点p,区间l~r上的第k个空位插入权重为w的点 void insert1(int p,int l,int r,int w); }; #endif
#include "stdafx.h" #include <iostream> #include "TreeBuild.h" TreeBuild::TreeBuild() { std::cin>>TreeBuild::n; left=new int[n+1]; right=new int[n+1]; m=new int[n+1]; ans=new int[n+1]; b=new int[n+1]; q=new int[n+1]; vis=new bool[n+1]; t=new int[n+1]; g=new int*[n+1]; for(int i=1;i<=n;i++) { g[i]=new int[i+1]; ans[i]=0; b[i]=0; q[i]=0; vis[i]=false; for(int j=1;j<=n;j++) g[j]=0; } //------------读入并构建树,树的边数等于n-1 int tempNodeIndex=0; int tempNodeChild=0; for(int i=1;i<n;i++) { std::cin>>tempNodeIndex>>tempNodeChild; b[tempNodeIndex]++; } //-------读入并构建t数组 for(int i=1;i<=n;i++) { std::cin>>t[i]; } dfs(1,1);//从根节点深度优先数,得到深度优先序列 for(int i=1;i<=n;i++) { find(1,1,n,(t[q[i]]+1),q[i]); } int *ansNode=new int[n+1]; for(int i=1;i<=n;i++) { ansNode[ans[i]]=i; } for(int i=1;i<=n;i++) { std::cout<<ansNode[i]<<'/t'; } std::cout<<std::endl; } void TreeBuild::dfs(int x, int index) { TreeBuild::vis[x]=true; q[index]=x; for(int i=1;i<=b[x];i++) { if(vis[g[x][i]]==false) { index++; dfs(g[x][i],index); } } } int TreeBuild::max(int x, int y) { return x>y?x:y; } void TreeBuild::build(int p, int l, int r) { if(l=r) { m[p]=1; left[p]=1; right[p]=1; return; } else { m[p]=r-l+1; left[p]=r-l+1; right[p]=r-l+1; int mid=(r+l)/2; build(p*2,l,mid); build(p*2+1,mid+1,r); } } void TreeBuild::find(int p, int l, int r, int k, int w) { if(m[p]==k) insert(p,l,r,k,w); else { int mid=(r+l)/2; if(m[2*p]>=k) find(2*p,l,mid,k,w); else if(m[2*p+1]>=k) find(2*p+1,mid+1,r,k,w); else if(right[2*p]+left[2*p+1]>=k) { find(2*p+1,mid,r,k-right[2*p],w); } left[p]=left[2*p]; if(left[2*p]==mid-l+1) left[p]+=left[2*p+1]; right[p]=right[2*p+1]; if(right[2*p+1]==r-mid) right[p]+=right[2*p]; m[p]=max(m[2*p],m[2*p+1]); m[p]=max(m[p],right[2*p]+left[2*p+1]); m[p]=max(m[p],max(left[p],right[p])); return; } } void TreeBuild::insert(int p,int l,int r,int k, int w) { if(r-l+1==k) insert1(p,l,r,w); else { int mid=(r+l)/2; if(m[2*p]>=k) insert(2*p,l,mid,k,w); else if(m[2*p+1]>=k) insert(2*p+1,mid+1,r,k,w); else if(right[2*p]+left[2*p+1]>=k) { insert(2*p+1,mid,r,k-right[2*p],w); } m[p]=max(m[2*p],m[2*p+1]); m[p]=max(m[p],right[2*p]+left[2*p+1]); m[p]=max(m[p],max(left[p],right[p])); return; } } void TreeBuild::insert1(int p, int l, int r, int w) { if(l==r) { m[p]=0;left[p]=0;right[p]=0; ans[r]=w; return; } else { int mid=(r+l)/2; insert1(2*p+1,mid+1,r,w); m[p]=m[p]-1; right[p]=0; left[p]--; } }