Hduoj1754【线段树】

/*I Hate It
Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 41131    Accepted Submission(s): 16306

Problem Description
很多学校流行一种比较的习惯。老师们很喜欢询问,从某某到某某当中,分数最高的是多少。
这让很多学生很反感。

不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问。当然,老师有时候需要更新某位同学的成绩。

Input
本题目包含多组测试,请处理到文件结束。
在每个测试的第一行,有两个正整数 N 和 M ( 0<N<=200000,0<M<5000 ),分别代表学生的数目和操作的数目。
学生ID编号分别从1编到N。
第二行包含N个整数,代表这N个学生的初始成绩,其中第i个数代表ID为i的学生的成绩。
接下来有M行。每一行有一个字符 C (只取'Q'或'U') ,和两个正整数A,B。
当C为'Q'的时候,表示这是一条询问操作,它询问ID从A到B(包括A,B)的学生当中,成绩最高的是多少。
当C为'U'的时候,表示这是一条更新操作,要求把ID为A的学生的成绩更改为B。


Output
对于每一次询问操作,在一行里面输出最高成绩。
 

Sample Input
5 6
1 2 3 4 5
Q 1 5
U 3 6
Q 3 4
Q 4 5
U 2 9
Q 1 5
 

Sample Output
5
6
5
9

HintHuge input,the C function scanf() will work better than cin 
 

Author
linle

Source
2007省赛集训队练习赛(6)_linle专场 
 

Recommend
lcy   |   We have carefully selected several similar problems for you:  1698 1542 1394 2795 1540 
*/
#include<stdio.h>
struct node
{
	int l, r, g;
}node[800100];
int fa[200022],  Max;
void buildtree(int i, int left, int right)//建造一个空的二叉树 
{
	node[i].l = left;
	node[i].r = right;
	node[i].g = 0;  //将树中的值全部初始化为0 
	if(left == right)//如果左右相等,说明已经到了二叉树的底部某个位置,可以存原始数组中的某个值 
	{
		fa[left] = i;//fa保存原始数组中的固定位置的值在二叉树中的位置 left即为原始数组中的第几个数i即为它在二叉树中的位置 
		return;
	}
	buildtree(i<<1, left, (left+right)/2);//分别两边建树 
	buildtree((i<<1)+1, (left+right)/2+1, right);
}

void update(int x)//将二叉树中x处的值更新为num 
{
	if(x == 1)  return;//编号为1已经更新到根节点 
	int f = x>>1;      //编号除2,求出父节点的编号 
	int a = node[f<<1].g;  //求出该父节点的两个儿子的值 
	int b = node[(f<<1)+1].g;
	node[f].g = a>b?a:b;//父节点的值更新为其中较大的值 
	update(f); //递归往上更新 
}

void query(int i, int x, int y)
{
	if(node[i].l == x && node[i].r == y)//如果区间找到,则比较最大值 
	{
		Max = Max > node[i].g?Max:node[i].g;
		return ;
	}
	i <<= 1;// 找出子节点 
	if(x <= node[i].r )
	{
		if(y <= node[i].r)//子区间在左子树中
		query(i, x, y);
		else
		query(i, x, node[i].r);//更改查询上限 
	} 
	i++;
	if(y >= node[i].l)
	{
		if(x >= node[i].l)//区间包含
		query(i, x, y);
		else
		query(i, node[i].l, y);//更改下限继续查询 
	}
} 
int main()
{
	int n, m, x, y, t;
	char ch;
	while(scanf("%d%d", &n, &m) != EOF)
	{
		buildtree(1, 1, n);//以1号节点为根节点建立二叉树 
		for(int i = 1; i <= n; i++)
		{
			scanf("%d", &t);
			node[fa[i]].g = t;//保存更改值 
			update(fa[i]);//更新二叉树 
		} 
		while(m--)
		{
			getchar();
			scanf("%c %d %d", &ch, &x, &y);
			if(ch == 'Q')
			{
				Max = 0;
				query(1, x,y);
				printf("%d\n",Max); 
			}
			else
			{
				node[fa[x]].g = y;//更新线段树中的最底部的x处的值 
				update(fa[x]);//由于底部更新,则二叉树上面的全部更新 
			}
		}
	}
}





//树状数组超时代码 
/*#include<stdio.h>
#include<string.h>
int a[200010], c[200010], n;
void update(int x, int num, int n)
{
	while(x <= n)
	{
		c[x] =  c[x]>num?c[x]:num;
		x += x&(-x);
	}
}
int query(int x, int y)
{
	int max = 0, j;
	j = y;
	while(j >= x)
	{
		if(j - j&(-j) >= x-1)//如果此数涵盖区间在查询区间内  寻找最大值 
		{
			max = max>c[j]?max:c[j];
			j -= j&(-j);
		}
		else // 否则比较原数组中的单个值 
		{
			max = max>a[j]?max:a[j];
			j--;
		}
	} 
	return max;
}
int main()
{
	int i, j, m;
	while(scanf("%d%d", &n, &m) != EOF)
	{
		memset(c, 0,sizeof(c));
		for(i = 1; i <= n; i++)
		{
			scanf("%d", &a[i]);
			if(i&1)
			c[i] = a[i];
			else
			update(i, a[i], n);
		}
		char ch;
		int max = 0;
		while(m--)
		{
			getchar();
			scanf("%c %d %d", &ch, &i, &j);
			if(ch == 'U')
			{
				update(i, j, n);
				a[i] = j;
			}
			else
			{
				max = query(i,j);
				printf("%d\n", max);
			} 
		}
	}
	return 0;
}*/

题意:给出一序列数字,有两种操作,一时更改序列中的值,一个是查询一段区间内的最大值。输出查询时的最大值。

思路:标准的线段树模板题,代码中注释也比较详细了。

关键:需要弄清楚二叉树中的编号与左右区间的的关系,刚开始学容易弄混。

你可能感兴趣的:(c)