问题描述
有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
1 2 3 4
2 1 3
1 4 3
3 1 4
样例输出
6
3
3
数据规模与约定
对于20%的数据n <= 100,m <= 200。
对于50%的数据n <= 5000,m <= 5000。
对于100%的数据1 <= n <= 100000,m <= 100000,0 <= 格子权值 <= 10000。
----------------------------------------------------------------------------------------
这道题看起来挺容易,就是操作数组,所以一开始我只是用简简单单的数组操作做这道题,结果运行超时;最后我用了树状数组来做这道题,也是超时。查了别人用同样的算法却不会超时,郁闷。
先附上树状数组的解法
import java.util.Scanner;
public class Main {
static Scanner reader = new Scanner(System.in);
static treePoint[] tree = null;
static int n;
static int m;
static int[] subPoint;
static int step = 0;
public static void main(String[] args) {
n = reader.nextInt();
m = reader.nextInt();
tree = new treePoint[(n*4) ];//树状数组这里要定义结点的四倍
subPoint = new int[n];//保存下标
int[][] operate = new int[m][3];//保存输入的操作
build(0, 0, n - 1);//建立树状树
int max;
int sum;
for (int i = 0; i < n; i++) {
int temp = reader.nextInt();
tree[subPoint[i]].max = temp;
tree[subPoint[i]].sum = temp;
}
//更新节点信息,主要是因为叶子节点数值修改了,那么其祖辈也需要更改
for (int i = 0; i < n; i++) {
upPoint(subPoint[i]);
}
//读取操作
for (int i = 0; i < m; i++) {
operate[i][0] = reader.nextInt();
operate[i][1] = reader.nextInt();
operate[i][2] = reader.nextInt();
}
//对操作进行判断输出
for (int i = 0; i < m; i++) {
//更改叶子节点信息
if (operate[i][0] == 1) {
tree[subPoint[operate[i][1] - 1]].sum = operate[i][2];
tree[subPoint[operate[i][1] - 1]].max = operate[i][2];
upPoint(subPoint[operate[i][1] - 1]);//更新祖辈
}
//得到区间的和
else if (operate[i][0] == 2) {
sum = getSum(0, operate[i][1] - 1, operate[i][2] - 1);
System.out.println(sum);
}
//得到区间的最大值
else if (operate[i][0] == 3) {
max = getMax(0, operate[i][1] - 1, operate[i][2] - 1);
System.out.println(max);
}
}
}
//求区间和的函数
public static int getSum(int i, int left, int right) {
//如果该节点的区间[tree[i].left,tree[i].right]落在[left,right]中,那么返回其区间的和,不需要在向下搜索
if (tree[i].left >= left && tree[i].right <= right) {
return tree[i].sum;
}
//如果该节点的区间[tree[i].left,tree[i].right]落在[left,right]的右区间,那么在右区间向下搜索
else if ((tree[i].left + tree[i].right) / 2 < left) {
return getSum((i << 1) + 2, left, right);
}
//如果该节点的区间[tree[i].left,tree[i].right]落在[left,right]的右区间,那么在左区间向下搜索
else if ((tree[i].left + tree[i].right) / 2 >= right) {
return getSum((i << 1) + 1, left, right);
}
//如果以上条件不符合,那么就落在区间中间
else {
return getSum((i << 1) + 2, left, right) + getSum((i << 1) + 1, left, right);
}
}
//求区间的最大值,与求区间和的原理类似
public static int getMax(int i, int left, int right) {
if (tree[i].left >= left && tree[i].right <= right) {
return tree[i].max;
} else if ((tree[i].left + tree[i].right) / 2 < left) {
return getMax((i << 1) + 2, left, right);
} else if ((tree[i].left + tree[i].right) / 2 >= right) {
return getMax((i << 1) + 1, left, right);
} else
return Math.max(getMax((i << 1) + 2, left, right), getMax((i << 1) + 1, left, right));
}
//更新祖辈信息
public static void upPoint(int i) {
//不是叶子节点就更新
if (tree[i].left != tree[i].right) {
tree[i].max = Math.max(tree[(i << 1) + 1].max, tree[(i << 1) + 2].max);
tree[i].sum = tree[(i << 1) + 1].sum + tree[(i << 1) + 2].sum;
}
//到根节点结束
if (i == 0)
return;
//向父亲节点更新,递归操作
upPoint((i - 1) / 2);
}
//建树
public static void build(int i, int left, int right) {
tree[i] = new treePoint();
tree[i].left = left;
tree[i].right = right;
if (left == right) {
subPoint[step++] = i;//保存叶子节点的信息
return;
} else {
build((i << 1) + 1, left, (left + right) / 2);//建立左区间
build((i << 1) + 2, (left + right) / 2 + 1, right);//建立右区间
}
}
}
class treePoint {
int left = 0;
int right = 0;
int max;
int sum;
}
运行结果是30分,下面我附上简单的数组操作的写法
import java.util.Scanner;
public class Main{
public static void main(String[] args)
{
Scanner reader=new Scanner(System.in);
int n=reader.nextInt();
int m=reader.nextInt();
int[] arr=new int[n];
int[][] open=new int[m][3];
for(int i=0;i<n;i++)
{
arr[i]=reader.nextInt();
}
for(int i=0;i<m;i++)
{
open[i][0]=reader.nextInt();
open[i][1]=reader.nextInt();
open[i][2]=reader.nextInt();
}
for(int i=0;i<m;i++)
{
if(open[i][0]==1)
{
arr[open[i][1]-1]=open[i][2];
}
else if(open[i][0]==2)
{
int result=0;
for(int s=open[i][1]-1;s<open[i][2];s++)
{
result+=arr[s];
}
System.out.println(result);
}
else if(open[i][0]==3)
{
int max=Integer.MIN_VALUE;
for(int s=open[i][1]-1;s<open[i][2];s++)
{
if(arr[s]>max)
max=arr[s];
}
System.out.println(max);
}
}
}
}