1、线段树详解C+版线段树的定义及特征定义1:线段树线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。一棵二叉树,记为T (a,b),参数a,b表示该节点表示区间a,b。区间的长度b-a记为L。递归定义Ta,b:若L0 :a, (a+b) div 2为 T的左儿子(a+b) div 2+1,b为T的右儿子。 若L=0 :T为一个叶子节点。 表示区间1, 5的线段树表示如下:线段树支持的操作有:构造,插入,查找,更新,删除;这些操作不一定都有,在实现上也不一定都是一个套路,这都取决于实际问题;实际上,通过在线段树节点上记录不同的信息,线段树可
2、以完成很多不同的任务;线段树的高度为logn,这也就使得线段树可以在O(lgn)的时间完成插入、查询、删除等操作。1、忠诚(TYVJ 1038)Description 老管家是一个聪明能干的人。他为财主工作了整整10年,财主为了让自已账目更加清楚。要求管家每天记k次账,由于管家聪明能干,因而管家总是让财主十分满意。但是由于一些人的挑拨,财主还是对管家产生了怀疑。于是他决定用一种特别的方法来判断管家的忠诚,他把每次的账目按1,2,3编号,然后不定时的问管家问题,问题是这样的:在a到b号账中最少的一笔是多少?为了让管家没时间作假他总是一次问多个问题。Input输入中第一行有两个数m,n表示有m(m
3、 =100000)笔账,n表示有n个问题,n =100000。 第二行为m个数,分别是账目的钱数 后面n行分别是n个问题,每行有2个数字说明开始结束的账目编号。Output输出文件中为每个问题的答案。具体查看样例。Sample Input10 31 2 3 4 5 6 7 8 9 102 73 91 10Sample Output2 3 1参考程序:#include #include #include using namespace std;int n,m,i,k,x,y,num,a100000,f100000;struct ss int l,r,ls,rs,f,data;t400001;vo
4、id build(int l,int r) int h; num+; h=num; th.l=l; th.r=r; if(l!=r) th.ls=num+1; tnum+1.f=h; build(l,(l+r)/2); th.rs=num+1; tnum+1.f=h; build(l+r)/2)+1,r); th.data=min(tth.ls.data,tth.rs.data); else th.data=al;fl=h; int find(int h,int p,int q) int v; if(th.l=p)&(th.r=q) return(th.data); v=(th.l+th.r)
5、/2; if(qv) return(find(th.rs,p,q); return(min(find(th.ls,p,v),find(th.rs,v+1,q);int main() num=0; t1.f=0; scanf(%d%d,&m,&n); for(i=1;i=m;i+) scanf(%d,&ai); build(1,m); for(i=1;i=n-1;i+) scanf(%d%d,&x,&y); printf(%d ,find(1,x,y); scanf(%d%d,&x,&y); printf(%d,find(1,x,y); return 0; 2、忠诚2(TYVJ 1039)Des
6、cription 老管家是一个聪明能干的人。他为财主工作了整整10年,财主为了让自已账目更加清楚。要求管家每天记k次账,由于管家聪明能干,因而管家总是让财主十分满意。但是由于一些人的挑拨,财主还是对管家产生了怀疑。于是他决定用一种特别的方法来判断管家的忠诚,他把每次的账目按1,2,3编号,然后不定时的问管家问题,问题是这样的:在a到b号账中最少的一笔是多少?为了让管家没时间作假他总是一次问多个问题。 在询问过程中账本的内容可能会被修改Input输入中第一行有两个数m,n表示有m(m =100000)笔账,n表示有n个问题,n =100000。 接下来每行为3个数字,第一个p为数字1或数字2,第
7、二个数为x,第三个数为y 当p=1 则查询x,y区间当p=2 则改变第x个数为yOutput输出文件中为每个问题的答案。具体查看样例。Sample Input10 31 2 3 4 5 6 7 8 9 101 2 72 2 01 1 10Sample Output2 0题解一:#include #include #include using namespace std;int n,m,i,k,x,y,num,a100000,f100000;struct ss int l,r,ls,rs,f,data;t400001;void build(int l,int r) int h; num+; h=
8、num; th.l=l; th.r=r; if(l!=r) th.ls=num+1; tnum+1.f=h; build(l,(l+r)/2); th.rs=num+1; tnum+1.f=h; build(l+r)/2)+1,r); th.data=min(tth.ls.data,tth.rs.data); else th.data=al;fl=h; void rebuild(int x) if(x=0) return; if(tx.data!=min(ttx.ls.data,ttx.rs.data) tx.data=min(ttx.ls.data,ttx.rs.data); rebuild
9、(tx.f); void change(int m,int x) tfm.data=x; rebuild(tfm.f);int find(int h,int p,int q) int v; if(th.l=p)&(th.r=q) return(th.data); v=(th.l+th.r)/2; if(qv) return(find(th.rs,p,q); return(min(find(th.ls,p,v),find(th.rs,v+1,q);int main() int flag=1; num=0; t1.f=0; scanf(%d%d,&m,&n); for(i=1;i=m;i+) sc
10、anf(%d,&ai); build(1,m); for(i=1;i=n;i+) scanf(%d,&k); if(k=1) if(flag=1) scanf(%d%d,&x,&y); printf(%d,find(1,x,y); flag=0; else scanf(%d%d,&x,&y); printf( %d,find(1,x,y); if(k=2) scanf(%d%d,&x,&y); change(x,y); return 0; 3、线段树练习题目描述 :一行N个方格,开始每个格子里都有一个整数。现在动态地提出一些问题和修改:提问的形式是求某一个特定的子区间a,b中所有元素的和;修改
11、的规则是指定某一个格子x,加上或者减去一个特定的值A。现在要求你能对每个提问作出正确的回答。1N100000,,提问和修改的总数m10000条。输入描述 Input Description输入文件第一行为一个整数N,接下来是n行n个整数,表示格子中原来的整数。接下一个正整数m,再接下来有m行,表示m个询问,第一个整数表示询问代号,询问代号1表示增加,后面的两个数x和A表示给位置X上的数值增加A,询问代号2表示区间求和,后面两个整数表示a和b,表示要求a,b之间的区间和。输出描述 Output Description共m行,每个整数样例输入 Sample Input64 5 6 2 1 341
12、3 52 1 41 1 92 2 6样例输出 Sample Output2222数据范围及提示 Data Size & Hint1N100000, m10000 。参考程序:#include#includeusing namespace std;struct tree int l; int r; int ls; int rs; int sum;t500000;int n,m,i,k,x,y,num,a100000,f100000;bool flag;void build(int p1,int l,int r) int m,ii; tp1.l=l; tp1.r=r; m=(tp1.l+tp1.r
13、)/2; for(ii=l;ii=r;ii+) tp1.sum+=aii; if(l=r) return; build(2*p1,l,m); build(2*p1+1,m+1,r);void modify(int p,int x1,int y1) int m; if(tp.l=x1) tp.sum+=y1; if(tp.l=tp.r) return; m=(tp.l+tp.r)/2; if(m=x1) modify(2*p,x1,y1); else modify(2*p+1,x1,y1);int find(int h,int p,int q) int m; if(th.l=p)&(th.r=q
14、) return(th.sum); m=(th.l+th.r)/2; if(q=m+1) return(find(2*h+1,p,q); else return(find(2*h,p,m)+find(2*h+1,m+1,q);int main() scanf(%d,&m); for(i=1;i=m;i+) scanf(%d,&ai); build(1,1,m); scanf(%d,&n); for(i=1;i=n;i+) scanf(%d,&k); if(k=2) scanf(%d%d,&x,&y); printf(%dn,find(1,x,y); if(k=1) scanf(%d%d,&x,
15、&y); modify(1,x,y); return 0;4、线段树练习 2题目描述 Description给你N个数,有两种操作1:给区间a,b的所有数都增加X2:询问第i个数是什么?输入描述 Input Description第一行一个正整数n,接下来n行n个整数,再接下来一个正整数Q,表示操作的个数. 接下来Q行每行若干个整数。如果第一个数是1,后接3个正整数a,b,X,表示在区间a,b内每个数增加X,如果是2,后面跟1个整数i, 表示询问第i个位置的数是多少。输出描述 Output Description对于每个询问输出一行一个答案样例输入 Sample Input312321 2 3
16、 22 3样例输出 Sample Output5数据范围及提示 Data Size & Hint数据范围1=n=1000001=q=100000参考程序:#include#includeusing namespace std;int n,q,a100001;struct data int l,r,x;tr400001;void build(int k,int s,int t) trk.l=s;trk.r=t; if(s=t)trk.x=as;return; int mid=(s+t)1; build(k1,s,mid); build(k1; if(b=mid)update(kmid)updat
17、e(k1|1,a,b,x); else update(k1,a,mid,x); update(k1; if(x=mid)return trk.x+ask(k1,x); else return trk.x+ask(k1|1,x); int main() scanf(%d,&n); for(int i=1;i=n;i+) scanf(%d,&ai); build(1,1,n); scanf(%d,&q); for(int i=1;i=q;i+) int t,a,b,x; scanf(%d,&t); if(t=1) scanf(%d%d%d,&a,&b,&x); update(1,a,b,x); e
18、lse scanf(%d,&x); printf(%dn,ask(1,x); return 0;4.1 线段树练习 3题目描述 Description给你N个数,有两种操作:1:给区间a,b的所有数增加X2:询问区间a,b的数的和。输入描述 Input Description第一行一个正整数n,接下来n行n个整数,再接下来一个正整数Q,每行表示操作的个数,如果第一个数是1,后接3个正整数,表示在区间a,b内每个数增加X,如果是2,表示操作2询问区间a,b的和是多少。pascal选手请不要使用readln读入输出描述 Output Description对于每个询问输出一行一个答案样例输入 Sa
19、mple Input312321 2 3 22 2 3样例输出 Sample Output9数据范围及提示 Data Size & Hint数据范围1=n=2000001=q=200000参考程序:#include#includeusing namespace std;int n,q,a200001;struct data int l,r; long long sum; int tag;tr800001;void build(int k,int s,int t) trk.l=s;trk.r=t; if(s=t)trk.sum=as;return; int mid=(s+t)1; build(k
20、1,s,mid); build(k1|1,mid+1,t); trk.sum=trk1.sum+trk1|1.sum;void pushdown(int k) int x=trk.r-trk.l+1; trk1.tag+=trk.tag; trk1|1.tag+=trk.tag; trk1)*trk.tag; trk1)*trk.tag; trk.tag=0;void update(int k,int a,int b,int x) int l=trk.l,r=trk.r; if(a=l&r=b) trk.tag+=x; trk.sum+=(b-a+1)*x; return; if(trk.ta
21、g)pushdown(k); int mid=(l+r)1; if(b=mid)update(kmid)update(k1|1,a,b,x); else update(k1,a,mid,x); update(k1|1,mid+1,b,x); trk.sum=trk1.sum+trk1; if(b=mid)return ask(kmid)return ask(k1|1,a,b); else return (ask(k1,a,mid)+ask(k1|1,mid+1,b); int main() scanf(%d,&n); for(int i=1;i=n;i+) scanf(%d,&ai); bui
22、ld(1,1,n); scanf(%d,&q); for(int i=1;i=q;i+) int t,a,b,x; scanf(%d,&t); if(t=1) scanf(%d%d%d,&a,&b,&x); update(1,a,b,x); else scanf(%d%d,&a,&b); printf(%lldn,ask(1,a,b); return 0;5、简单题(easy)有一个n个元素的数组,每个元素初始均为0。有m条指令,要么让其中一段连续序列数字反转0变1,1变0(操作1),要么询问某个元素的值(操作2)。例如当n=20时,10条指令如下:操作回答操作后的数组1 1 10N/A111
23、111111100000000002 61111111111100000000002 120111111111100000000001 5 12N/A111100000011000000002 60111100000011000000002 150111100000011000000001 6 16N/A111101111100111100001 11 17N/A111101*010002 121111101111111000010002 6111110111111100001000【输入文件】输入文件easy.in第一行包含两个整数n,m,表示数组的长度和指令的条数,以下m行,每行的第一个数
24、t表示操作的种类。若t=1,则接下来有两个数L, R (L=R),表示区间L, R的每个数均反转;若t=2,则接下来只有一个数I,表示询问的下标。【输出文件】 每个操作2输出一行(非0即1),表示每次操作2的回答。【样例】easy.ineasy.out20 101 1 102 62 121 5 122 62 151 6 161 11 172 122 6100011【限制】50%的数据满足:1=n=1,000,1=m=10,000100%的数据满足:1=n=100,000,1=m=500,000这道题标准算法是用线段树来实现#include#include#include#include#include#include#includeusing namespace std;const int maxn=100005;struct node int a,b; bool lazy; Treemaxn*4;int N,M;void yin(int &x)
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1