**********(北大)线段树 **********

 

目录

样例引入:POJ 3264 Balanced Lineup

士兵杀敌(一)

士兵杀敌(二)

士兵杀敌(三)

 NYOJ 123 士兵杀敌(四)

POJ 3468 A Simple Problem with Integers

秋实大哥与花

 


 


          秋实大哥与花  ::http://qscoj.cn/#/problem/show/1057   https://blog.csdn.net/qq_37275680/article/category/7194362

POJ 2528 Mayor's posters

POJ 1151 Atlantis

POJ 3321 Apple Tree

POJ题目推荐:
2182, 2352, 1177, 3667,3067

样例引入:POJ 3264 Balanced Lineup

Description

For the daily milking, Farmer John's N cows (1 ≤ N ≤ 50,000) always line up in the same order. One day Farmer John decides to organize a game of Ultimate Frisbee with some of the cows. To keep things simple, he will take a contiguous range of cows from the milking lineup to play the game. However, for all the cows to have fun they should not differ too much in height.

Farmer John has made a list of Q (1 ≤ Q ≤ 200,000) potential groups of cows and their heights (1 ≤ height ≤ 1,000,000). For each group, he wants your help to determine the difference in height between the shortest and the tallest cow in the group.

Input

Line 1: Two space-separated integers, N and Q
Lines 2..N+1: Line i+1 contains a single integer that is the height of cow i 
Lines N+2..N+Q+1: Two integers A and B (1 ≤ A ≤ B ≤ N), representing the range of cows from A to B inclusive.

Output

Lines 1..Q: Each line contains a single integer that is a response to a reply and indicates the difference in height between the tallest and shortest cow in the range.

Sample Input

6 3
1
7
3
4
2
5
1 5
4 6
2 2

Sample Output

6
3
0

Source

USACO 2007 January Silver

**********(北大)线段树 **********_第1张图片

 建树过程描述:

从传进来的参数l=1,r=6;不满足l==r这一条件,被不断二分,不断递归调用,直到 l==r,此时找到了叶子节点中,最大值和最小值是一样的,return跳出,跳出之后,再去找右边分支的最大值与最小值,这样从叶子节点往上找,直到祖先根节点。

一:叶子节点的最大值与最小值找到了,其根节点的最大值与最小值就直接取决于叶子节点中最大值与最小值。

二:左子树与右子树中最大值与最小值找到了,那么整个序列的最大值与最小值就存在了祖先根节点中了。

三:由下往上推出整个序列的最大值和最小值。

void build(int d,int l,int r)
{
	Tree[d].l=l,Tree[d].r=r;
	if(l==r)
	{
		Tree[d].minn=Tree[d].maxn=b[l];
		return ;
	}
	int mid=l+(r-l)/2;
	build(2*d,l,mid);
	
	//printf("l = %d,mid =  %d\n",l,mid);
	
	build(2*d+1,mid+1,r);
	
	//printf("mid+1 = %d,r = %d\n",mid+1,r);
	//pushup(d);
	Tree[d].maxn=max(Tree[2*d].maxn,Tree[2*d+1].maxn);
	Tree[d].minn=min(Tree[2*d].minn,Tree[2*d+1].minn);
}

**********(北大)线段树 **********_第2张图片

 

下标 1 2 3 4 5 6
b数组 1 7 3 4 2 5
Tree d: 1 2 3 4 5 6 7 8 9 10 11
minn: 1 1 2 1 3 2 5 1 7 4 2
maxn: 7 7 5 7 3 4 5 1 7 4 2
l: 1 1 4 1 3 4 6 1 2 4 5
r: 6 3 6 2 3 5 6 1 2 4 5

 **********(北大)线段树 **********_第3张图片

 **********(北大)线段树 **********_第4张图片

**********(北大)线段树 **********_第5张图片

**********(北大)线段树 **********_第6张图片

**********(北大)线段树 **********_第7张图片

#include
#include
#include
#include
#include
#include
using namespace std;
#define INF 0x3f3f3f3f
const int MAX=201000;
struct Node
{
	int l,r;
	int minn,maxn;
}Tree[4*MAX];
int b[MAX];
int minV=INF;
int maxV=-INF;
void pushup(int d) //求区间元素最大最小值
{
	Tree[d].maxn=max(Tree[2*d].maxn,Tree[2*d+1].maxn);
	Tree[d].minn=min(Tree[2*d].minn,Tree[2*d+1].minn);
}
void build(int d,int l,int r)//建树
{
	Tree[d].l=l,Tree[d].r=r;
	if(l==r)
	{
		Tree[d].minn=Tree[d].maxn=b[l];
		return ;
	}
	int mid=l+(r-l)/2;
	build(2*d,l,mid);
	build(2*d+1,mid+1,r);
	pushup(d);
}
void query(int d,int l,int r,int x,int y)//查询
{
	if(Tree[d].maxn<=maxV && Tree[d].minn>=minV)//剪枝,
//当找到当前的节点的最大值不大于maxV,最小值不小于minV,就没必要找了,因为区间根节点的值都称不上最值,叶子节点更不行 
	     return ;
	if(l==x && r==y)
	{
		minV=min(minV,Tree[d].minn);
		maxV=max(maxV,Tree[d].maxn);
		return ;
	}
	int mid=l+(r-l)/2;
	if(mid>=y)
	{
		query(2*d,l,mid,x,y);
	}
	else if(mid

注意:

1.线段树的建树过程就是一个二分,分到一个元素的时候再回溯。

2.mid总是忘记+1 。

士兵杀敌(一)

描述

南将军手下有N个士兵,分别编号1到N,这些士兵的杀敌数都是已知的。

小工是南将军手下的军师,南将军现在想知道第m号到第n号士兵的总杀敌数,请你帮助小工来回答南将军吧。

注意,南将军可能会问很多次问题。

输入

只有一组测试数据
第一行是两个整数N,M,其中N表示士兵的个数(1 随后的一行是N个整数,ai表示第i号士兵杀敌数目。(0<=ai<=100)
随后的M行每行有两个整数m,n,表示南将军想知道第m号到第n号士兵的总杀敌数(1<=m,n<=N)。

输出

对于每一个询问,输出总杀敌数
每个输出占一行

样例输入

5 2
1 2 3 4 5
1 3
2 4

样例输出

6
9

来源

[张云聪]原创

典型的线段树问题,求区间和。

线段树是一颗完美二叉树,每个节点要么是叶子节点要么有2个儿子的树

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn=1e6+10;
int n,m;
int x,y;
struct Node
{
	int l,r,sum;
}Tree[maxn*4];  //线段树的数组是数据的4倍  
void pushup(int d)
{
	Tree[d].sum=Tree[2*d].sum+Tree[2*d+1].sum;//求和时,下标总是忘记变换
}
void build(int d,int l,int r)
{
	Tree[d].l=l,Tree[d].r=r;
	if(l==r)
	{
		int t;
		scanf("%d",&t);
		Tree[d].sum=t;
		return ;
	}
	int mid=l+(r-l)/2;
	build(2*d,l,mid);
	build(2*d+1,mid+1,r);
	pushup(d);
}

int kill(int d,int l,int r,int x,int y)
{
	if(l==x&&r==y)
	{
		return Tree[d].sum;
	}
	int mid=l+(r-l)/2;
    if(mid>=y)
    {
    	return kill(2*d,l,mid,x,y);
	}else if(mid

注意:

1.建线段树开的数组是数据的4倍,总是把数组开小。

2.在求和时,每一个节点从叶子节点开上往上返回,父亲节点是两叶子节点的和,下标不要忘记变换。

3.在求区间x到y的和时,在判断mid与x与y比较的过程中,mid比y大了,证明区间在左子树上,如果mid比x小了;证明区间在右子树上;如果在区间之内,就从x到mid,再从mid+1到y,求解过程就是不断的把一棵树二分,一直分到最小并且是要找的那个区间为止。

4.不要忘记对mid做+1操作。

士兵杀敌(二)

描述

南将军手下有N个士兵,分别编号1到N,这些士兵的杀敌数都是已知的。

小工是南将军手下的军师,南将军经常想知道第m号到第n号士兵的总杀敌数,请你帮助小工来回答南将军吧。

南将军的某次询问之后士兵i可能又杀敌q人,之后南将军再询问的时候,需要考虑到新增的杀敌数。

 

输入

只有一组测试数据
第一行是两个整数N,M,其中N表示士兵的个数(1 随后的一行是N个整数,ai表示第i号士兵杀敌数目。(0<=ai<=100)
随后的M行每行是一条指令,这条指令包含了一个字符串和两个整数,首先是一个字符串,如果是字符串QUERY则表示南将军进行了查询操作,后面的两个整数m,n,表示查询的起始与终止士兵编号;如果是字符串ADD则后面跟的两个整数I,A(1<=I<=N,1<=A<=100),表示第I个士兵新增杀敌数为A.

输出

对于每次查询,输出一个整数R表示第m号士兵到第n号士兵的总杀敌数,每组输出占一行

样例输入

5 6
1 2 3 4 5
QUERY 1 3
ADD 1 2
QUERY 1 3
ADD 2 3
QUERY 1 2
QUERY 1 5

样例输出

6
8
8
20

来源

[张云聪]原创

涉及到区间求和与单点更新。

在士兵杀敌(一)的基础上增加了更新操作,不仅要算出区间x到y的士兵杀敌的人数,还要更新士兵的杀敌人数,

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const  int maxn=1e6+10;
const int maxnn=1e5+10;
int n,m;
char name[100];
int x,y;
struct  Node
{
	int l,r,sum;
}Tree[maxn<<2]; //开四倍数组
void pushup(int d)
{
	Tree[d].sum=Tree[d*2].sum+Tree[2*d+1].sum;
}
void update(int d,int l,int r,int x,int y)
{
	if(l==r)
	{
		Tree[d].sum=y+Tree[d].sum;
		return ;
	}
	int mid=(l+r)/2;
	if(mid>=x)
	   update(2*d,l,mid,x,y);
	else
	   update(2*d+1,mid+1,r,x,y);
	pushup(d);
}
void build(int d,int l,int r)
{
	Tree[d].l=l,Tree[d].r=r;
	if(l==r)
	{
		int t;
		scanf("%d",&t);
		Tree[d].sum=t;
		return ;
	}
	int mid=(l+r)/2;
	build(2*d,l,mid);
	build(2*d+1,mid+1,r);
	pushup(d);
}
int kill(int d,int l,int r,int x,int y)
{
	if(l==x && r==y)
	{
		return Tree[d].sum;
	}
	int mid = (l+r)/2;
	if(mid>=y)
	{
		return kill(2*d,l,mid,x,y);
	}
	else if(mid

注意:

1.数组开4倍数组。

2.递归结束出口return不要忘记。

士兵杀敌(三)

描述

南将军统率着N个士兵,士兵分别编号为1~N,南将军经常爱拿某一段编号内杀敌数最高的人与杀敌数最低的人进行比较,计算出两个人的杀敌数差值,用这种方法一方面能鼓舞杀敌数高的人,另一方面也算是批评杀敌数低的人,起到了很好的效果。

所以,南将军经常问军师小工第i号士兵到第j号士兵中,杀敌数最高的人与杀敌数最低的人之间军功差值是多少。

现在,请你写一个程序,帮小工回答南将军每次的询问吧。

注意,南将军可能询问很多次。

输入

只有一组测试数据
第一行是两个整数N,Q,其中N表示士兵的总数。Q表示南将军询问的次数。(1 随后的一行有N个整数Vi(0<=Vi<100000000),分别表示每个人的杀敌数。
再之后的Q行,每行有两个正正数m,n,表示南将军询问的是第m号士兵到第n号士兵。

输出

对于每次询问,输出第m号士兵到第n号士兵之间所有士兵杀敌数的最大值与最小值的差。

样例输入

5 2
1 2 6 9 3
1 2
2 4

样例输出

1
7

来源

经典改编

上传者

张云聪

代码与样例一致。

#include
#include
#include
#include
#include
#include
using namespace std;
#define INF 0x3f3f3f3f
const int MAX=201000;
struct Node
{
	int l,r;
	int minn,maxn;
}Tree[4*MAX];
int b[MAX];
int minV=INF;
int maxV=-INF;
void pushup(int d)
{
	Tree[d].maxn=max(Tree[2*d].maxn,Tree[2*d+1].maxn);
	Tree[d].minn=min(Tree[2*d].minn,Tree[2*d+1].minn);
}
void build(int d,int l,int r)
{
	Tree[d].l=l,Tree[d].r=r;
	if(l==r)
	{
		Tree[d].minn=Tree[d].maxn=b[l];
		return ;
	}
	int mid=l+(r-l)/2;
	build(2*d,l,mid);
	
	//printf("l = %d,mid =  %d\n",l,mid);
	
	build(2*d+1,mid+1,r);
	
	//printf("mid+1 = %d,r = %d\n",mid+1,r);
	//pushup(d);
	Tree[d].maxn=max(Tree[2*d].maxn,Tree[2*d+1].maxn);
	Tree[d].minn=min(Tree[2*d].minn,Tree[2*d+1].minn);
}
void query(int d,int l,int r,int x,int y)
{
	if(Tree[d].maxn<=maxV && Tree[d].minn>=minV)//剪枝 
	     return ;
	if(l==x && r==y)
	{
		minV=min(minV,Tree[d].minn);
		maxV=max(maxV,Tree[d].maxn);
		return ;
	}
	int mid=l+(r-l)/2;
	if(mid>=y)
	{
		query(2*d,l,mid,x,y);
	}
	else if(mid

 NYOJ 123 士兵杀敌(四)

 

描述

南将军麾下有百万精兵,现已知共有M个士兵,编号为1~M,每次有任务的时候,总会有一批编号连在一起人请战(编号相近的人经常在一块,相互之间比较熟悉),最终他们获得的军功,也将会平分到每个人身上,这样,有时候,计算他们中的哪一个人到底有多少军功就是一个比较困难的事情,军师小工的任务就是在南将军询问他某个人的军功的时候,快速的报出此人的军功,请你编写一个程序来帮助小工吧。

假设起始时所有人的军功都是0.

输入

只有一组测试数据。
每一行是两个整数T和M表示共有T条指令,M个士兵。(1<=T,M<=1000000)
随后的T行,每行是一个指令。
指令分为两种:
一种形如
ADD 100 500 55 表示,第100个人到第500个人请战,最终每人平均获得了55军功,每次每人获得的军功数不会超过100,不会低于-100。
第二种形如:
QUERY 300 表示南将军在询问第300个人的军功是多少。

输出

对于每次查询输出此人的军功,每个查询的输出占一行。

样例输入

4 10
ADD 1 3 10
QUERY 3
ADD 2 6 50
QUERY 3

样例输出

10
60

来源

[张云聪]原创

上传者

张云聪

这里有一个优化处理,因为这里的应用是区间更新,单点取值,
所以在插入数值的时候,不用访问到最底下的节点,遇到一个包含于插入区间的区间,就返回。

从上往下走,到了要增加军功的区间,就增加军功,是区间增加军功,不用增加到叶子节点。

查询点时,从上往下取点,具体到叶子节点。

**********(北大)线段树 **********_第8张图片

#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn=1010000;
int T,N;
struct Node
{
	int l,r;
	int sum;
}Tree[4*maxn];

/*void pushup(int d)
{
	//
	Tree[d].sum=Tree[2*d].sum+Tree[2*d+1].sum;
	//printf("Tree[%d].sum = %d\n",d,Tree[d].sum);
}*/
void build(int d,int l,int r)
{
	Tree[d].l=l,Tree[d].r=r;
	Tree[d].sum=0;
	if(l=l && Tree[d].r<=r)
	{
		Tree[d].sum+=value;
	//printf("Tree[%d].sum = %d\n",d,Tree[d].sum);		
		return ;
	}
	int mid=Tree[d].l+(Tree[d].r-Tree[d].l)/2;
	if(r<=mid)
	update(2*d,l,r,value);
	else if(mid

 注意:                                   蓝色部分要注意啊! 总是写错

if(r<=mid)
    update(2*d,
l,r,value);
    else if(mid     update(2*d+1,
l,r,value);

 

POJ 3468 A Simple Problem with Integers

Description

You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of operation is to add some given number to each number in a given interval. The other is to ask for the sum of numbers in a given interval.

Input

The first line contains two numbers N and Q. 1 ≤ N,Q ≤ 100000.
The second line contains N numbers, the initial values of A1, A2, ... , AN. -1000000000 ≤ Ai ≤ 1000000000.
Each of the next Q lines represents an operation.
"C a b c" means adding c to each of AaAa+1, ... , Ab. -10000 ≤ c ≤ 10000.
"Q a b" means querying the sum of AaAa+1, ... , Ab.

Output

You need to answer all Q commands in order. One answer in a line.

Sample Input

10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4

Sample Output

4
55
9
15

Hint

The sums may exceed the range of 32-bit integers.

Source

POJ Monthly--2007.11.25, Yang Yi

题意:

给定Q (1 ≤ Q ≤ 100,000)个数A1,A2 … AQ,, 以及可能多次进行的两个操作:
1)对某个区间Ai … Aj的每个数都加n(n可变)

2) 求某个区间Ai … Aj的数的和

思路:

1.在增加时,就调用update更新函数。

**********(北大)线段树 **********_第9张图片

2.在查询时,就调用query函数

**********(北大)线段树 **********_第10张图片

北大课件代码:

#include 
#include
#include
#include
#include
using namespace std;
struct CNode
{
	int L,R;
	CNode *pLeft,*pRight;
	long long nSum;//原来的和 
	long long lnc;//增量c的累加 
}Tree[200010];//2倍叶子节点数目就够了 
int nCount=0;
int Mid(CNode *pRoot)
{
	return (pRoot->L+pRoot->R)/2;
}
void BuildTree(CNode *pRoot,int L,int R)
{
    pRoot->L=L;
    pRoot->R=R;
    pRoot->nSum=0;
    pRoot->lnc=0;
    if(L==R)
    {
    	return ;
	}
	nCount++;
	pRoot->pLeft=Tree+nCount;
	nCount++;
	pRoot->pRight=Tree+nCount;
	BuildTree(pRoot->pLeft,L,(L+R)/2);
	BuildTree(pRoot->pRight,(L+R)/2+1,R);
}
void Insert(CNode *pRoot,int i,int v)
{
	if(pRoot->L==i && pRoot->R==i)
	{
		pRoot->nSum=v;
		return ;
	}
	pRoot->nSum+=v;
	if(i<=Mid(pRoot))
	   Insert(pRoot->pLeft,i,v);
	else
	   Insert(pRoot->pRight,i,v);
}
void Add(CNode *pRoot,int a,int b,long long c)
{
	if(pRoot->L==a && pRoot->R==b)
	{
		pRoot->lnc+=c;
		return ;
	}
	pRoot->nSum+=c*(b-a+1);
	if(b<=(pRoot->L+pRoot->R)/2)
	{
		Add(pRoot->pLeft,a,b,c);
	}
	else if(a>=(pRoot->L+pRoot->R)/2+1)
	{
	    Add(pRoot->pRight,a,b,c);	
	}
	else
	{
		Add(pRoot->pLeft,a,(pRoot->L+pRoot->R)/2,c);
		Add(pRoot->pRight,(pRoot->L+pRoot->R)/2+1,b,c);
	}
} 
long long QuerynSum(CNode *pRoot,int a,int b)
{
	if(pRoot->L==a && pRoot->R==b)
	{
		return pRoot->nSum+(pRoot->R-pRoot->L+1)*pRoot->lnc;
	}
	pRoot->nSum+=(pRoot->R-pRoot->L+1)*pRoot->lnc;
	Add(pRoot->pLeft,pRoot->L,Mid(pRoot),pRoot->lnc);
	Add(pRoot->pRight,Mid(pRoot)+1,pRoot->R,pRoot->lnc);
	pRoot->lnc=0;
	if(b<=Mid(pRoot))
	  return QuerynSum(pRoot->pLeft,a,b);
	else if(a>=Mid(pRoot)+1)
	  return QuerynSum(pRoot->pRight,a,b);
	else
	  return QuerynSum(pRoot->pLeft,a,Mid(pRoot))+QuerynSum(pRoot->pRight,Mid(pRoot)+1,b);
	 
}
int main()
{
	int n,q,a,b,c;
	char cmd[10];
	scanf("%d%d",&n,&q);
	nCount=0;
	BuildTree(Tree,1,n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a);
		Insert(Tree,i,a);
	}
	for(int i=0;i

代码不正确:一直改不出来

#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn=101000;
typedef long long ll;
struct Node
{
   int l,r;
   ll sum;
   ll lnc;
}Tree[4*maxn];
int b[maxn];
int n,q;
void pushup(int d)
{
	Tree[d].sum=Tree[2*d].sum+Tree[2*d+1].sum;
}
void build(int d,int l,int r)
{
	Tree[d].l=l,Tree[d].r=r;
	Tree[d].lnc=0;
	Tree[d].sum=0;
	if(l==r)
	{
	Tree[d].sum=b[l];
	Tree[d].lnc=0;
		return ;
	}
	int mid=l+(r-l)/2;
	build(2*d,l,mid);
	build(2*d+1,mid+1,r);
	pushup(d);
}
void update(int d,int x,int y,ll z)
{
	if(Tree[d].l==x && Tree[d].r==y)
	{
		Tree[d].lnc+=z;
		return ;
	}
	Tree[d].sum+=z*(y-x+1);
	
	int mid=Tree[d].l+(Tree[d].r-Tree[d].l)/2;
	if(mid>=y)
	{
		update(2*d,x,y,z);
	}
	else if(mid=y)
	{
		return query(2*d,x,y);
	}
	else if(mid

秋实大哥与花

https://blog.csdn.net/qq_37275680/article/category/7194362

 

你可能感兴趣的:(ACM--树)