操作格子
问题描述
有n个格子,从左到右放成一排,编号为1-n。
共有m次操作,有3种操作类型:
1.修改一个格子的权值,
2.求连续一段格子权值和,
3.求连续一段格子的最大值。
对于每个2、3操作输出你所求出的结果。
输入格式
第一行2个整数n,m。
接下来一行n个整数表示n个格子的初始权值。
接下来m行,每行3个整数p,x,y,p表示操作类型,p=1时表示修改格子x的权值为y,p=2时表示求区间[x,y]内格子权值和,p=3时表示求区间[x,y]内格子最大的权值。
输出格式
有若干行,行数等于p=2或3的操作总数。
每行1个整数,对应了每个p=2或3操作的结果。
样例输入
4 3
1 2 3 4
2 1 3
1 4 3
3 1 4
样例输出
6
3
数据规模与约定
对于20%的数据n <= 100,m <= 200。
对于50%的数据n <= 5000,m <= 5000。
对于100%的数据1 <= n <= 100000,m <= 100000,0 <= 格子权值 <= 10000。
#include <stdio.h>
#include <stdlib.h>
//定义结构体:线段树
typedef struct node
{
int max, sum; //统计线段树的最大值、和
int left,right; //线段树区间的左右值
struct node *lchild; //左子树
struct node *rchild; //右子树
}XNode;
//返回最大值
int maxValue(int max, int temp)
{
if (temp > max)
max = temp;
return max; //返回最大值
}
//创建线段树
XNode *CreateXTree(int left, int right) //传进区间左右值
{
XNode *xTree = (XNode *)malloc(sizeof(XNode));
xTree->left = left; //给左端赋值
xTree->right = right; //给右端赋值
xTree->max = 0; //线段树:结点维护内容
xTree->sum = 0;
xTree->lchild = NULL; //子树初始化 置空
xTree->rchild = NULL; //置空
if (right != left) //right != left 元区间
{
int mid = (left+right)/2; //区间中点
xTree->lchild = CreateXTree(left, mid); //创建左子树
xTree->rchild = CreateXTree(mid+1, right); //创建右子树
}
return xTree;
}
//插入一条线段
void Insert(XNode *xTree, int point, int value)
{
xTree->sum += value; //搜索树时,经过某区间 统计
xTree->max = maxValue(xTree->max,value);//maxValue返回最大值
if (xTree->left == xTree->right)//找到该线段
return;
else
{
if (point <= (xTree->left + xTree->right)/2)
Insert(xTree->lchild,point,value);//左搜索
else
Insert(xTree->rchild,point,value);//右搜索
}
return;
}
//1.修改格子权值
void Modify(XNode *xTree, int point, int value)
{
if (xTree->left == point && xTree->right == point) //找到该结点,修改
{
xTree->max = value;//修改最大值
xTree->sum = value;//修改和
return;
}
else
{
int mid = (xTree->left+xTree->right)/2;
if (point <= mid) //往左子树搜索
Modify(xTree->lchild,point,value);
else //往右子树搜索
Modify(xTree->rchild,point,value);
xTree->max = maxValue(xTree->lchild->max,xTree->rchild->max);//修改最大值:从下往上
xTree->sum = xTree->lchild->sum + xTree->rchild->sum; //修改和 :下->上
}
return;
}
//2.求连续一段格子权值和
int GeziSum(XNode *xTree, int left, int right)
{
if (left == xTree->left && right == xTree->right) //找到该线段
return xTree->sum;
else
{
int mid = (xTree->left+xTree->right)/2;
if (right <= mid) //往左子树搜索
return GeziSum(xTree->lchild,left,right);
else if (left > mid) //往右子树搜索
return GeziSum(xTree->rchild,left,right);
else //分叉:左右搜索"和"值
return GeziSum(xTree->lchild,left,mid) + GeziSum(xTree->rchild,mid+1,right);
}
}
//3.求连续一段格子的最大值
int GeziMax(XNode *xTree, int left, int right)
{
if (left == xTree->left && right == xTree->right) //找到该线段
return xTree->max;
else
{
int mid = (xTree->left+xTree->right)/2;
if (right <= mid) //往左子树搜索
return GeziMax(xTree->lchild,left,right);
else if (left > mid) //往右子树搜索
return GeziMax(xTree->rchild,left,right);
else //分叉:返回搜索到的最大值
return maxValue(GeziMax(xTree->lchild,left,mid),GeziMax(xTree->rchild,mid+1,right));
}
}
//主函数
int main()
{
int m = 0, n = 0, i = 0, j = 0;
XNode *xTree = NULL;
int input[100000][3] = {0}; //input[][0]:操作序号,input[][1]:x,input[][2]:y
int Gezi = 0;
scanf("%d%d",&n,&m); //n:格子个数, m:操作次数
xTree = CreateXTree(1,n); //创建线段树,区间:1~n
for (i = 1; i <= n; ++i) //给格子赋权值,Gezi;
{
scanf("%d",&Gezi);
Insert(xTree,i,Gezi); //给线段树赋值
}
for (i = 0; i < m; ++i)
for (j = 0; j < 3; ++j) //一个循环,输入3次:0,1,2
scanf("%d",&input[i][j]);
for (i = 0; i < m; ++i) //执行操作
{
switch(input[i][0])
{
case 1:Modify(xTree,input[i][1],input[i][2]);
break;//修改线段权值
case2:printf("%d\n",GeziSum(xTree,input[i][1],input[i][2]; break;//返回线段和
case3:printf("%d\n",GeziMax(xTree,input[i][1],input[i][2]; break;//返回线段最大值
default:break;
}
}
return 0;
}