您的位置:首页 > 其它

蓝桥杯 算法训练 操作格子

2015-02-25 18:47 471 查看


问题描述

有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。


解题思路

由数据规模得,使用int数据类型即可。

注意,区间计数是从1开始的。

按照一般的思路提交,可是超时,刚好看到题目锦囊提示使用线段树。

线段树(Segment Tree)是一种二叉搜索树,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。

对于线段树中的每一个非叶子节点[a,b],它的左子树表示的区间为[a,(a+b)/2],右子树表示的区间为[(a+b)/2+1,b]。因此线段树是平衡二叉树。叶节点数目为N,即整个线段区间的长度。

简单的说,顾名思义,就是将一段线段(如:1-10),不断的划分,形成一棵树。

在这里,使用数组存放满二叉树,那数组的大小是多少呢?

在构建和利用线段树的过程中,充分使用了分治的思想。

题目中说,“p=1时表示修改格子x的权值为y”,这个x代表的是格子的位置。

创建的是1到n的线段树,其实每个结点的权重是不需要额外存储的,只需知道权重的和与最大值。叶子结点的sum和max即为权重。

注意,数组从1开始存储,利于二叉树结点位置的确定。

找了好几个小时的错误,原来是在找最大值的地方,把right写成了left,悲哀!忽然有种高中做数学题的悲哀。


代码实现


数组或容器

只能满足前50%的数据。

注意,在case语句中最好不要定义并初始化变量,常引起错误。

case后加大括号,可定义局部变量。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main(void) {
int n, m;
cin >> n >> m;
vector<int> v;
while(n--) {
int tmp;
cin >> tmp;
v.push_back(tmp);
}
while(m--) {
int p, x, y;
cin >> p >> x >> y;
int sum = 0;
int max = 0;
switch(p) {
case 1 :
v[x-1] = y;
break;
case 2 :
for(int i = x; i <= y; i++) {
sum += v[i - 1];
}
cout << sum << endl;
break;
case 3 :
for(int i = x; i <= y; i++) {
if(v[i - 1] > max) {
max = v[i - 1];
}
}
cout << max << endl;
break;
default :
break;
}
}
return 0;
}


线段树

这里使用数组存放线段树,其实和线段树相关的操作只涉及到4种:

构建线段树
改变线段树中某个结点的值
求某区间的结点权重的和
求某区间的结点权重的最大值

只是存放线段树的数组的大小还没搞清楚,从coolnote下载的测试数据表明:

当n为100000时,最后一个不为0的数组索引为262142。

给定【1,n】,建立线性二叉树,其层数是确定的,根据二分原理,其层数为m=上取整(log(n))+1 , 那么数组上界为这棵满二叉树所有节点个数: 2^m-1=2^(上取整(log(n))+1)-1

举个例子,n=5,树层数为m=4,上界为2^4-1=15

n=6,树层数为m=4,上界为2^4-1=15

n=100000,树层数为m=18,上界为2^18-1=262143
#include <iostream>
#include <cstring>
using namespace std;

typedef struct SNode {
int max, sum;
int left, right;
} SNode;

SNode t[300000];

int two_max(int a, int b) {
return a > b ? a : b;
}

void build(int pos, int l, int r) {
t[pos].left = l;
t[pos].right = r;
t[pos].max = 0;
t[pos].sum = 0;
if(l == r) {
return;
}
build(pos * 2, l, (l + r) / 2);
build(pos * 2 + 1, (l + r) / 2 + 1, r);
}

void change(int pos, int v, int w) {
if(t[pos].left == v && t[pos].right == v) {
t[pos].sum = w;
t[pos].max = w;
return;
}
int middle = (t[pos].left + t[pos].right) / 2;
if(v <= middle) {
change(pos * 2, v, w);
} else {
change(pos * 2 + 1, v, w);
}
t[pos].sum = t[pos * 2].sum + t[pos * 2 + 1].sum;
t[pos].max = two_max(t[pos * 2].max, t[pos * 2 + 1].max);
}

int get_sum(int pos, int l, int r) {
if(t[pos].left == l && t[pos].right == r) {
return t[pos].sum;
}
int middle = (t[pos].left + t[pos].right) / 2;
if(r <= middle) {
return get_sum(pos * 2, l, r);
}
if(l > middle) {
return get_sum(pos * 2 + 1, l, r);
}
return get_sum(pos * 2, l, middle) + get_sum(pos * 2 + 1, middle + 1, r);
}

int get_max(int pos, int l, int r) {
if(t[pos].left == l && t[pos].right == r) {
return t[pos].max;
}
int middle = (t[pos].left + t[pos].right) / 2;
if(r <= middle) {
return get_max(pos * 2, l, r);
}
if(l > middle) {
return get_max(pos * 2 + 1, l, r);
}
return two_max(get_max(pos * 2, l, middle), get_max(pos * 2 + 1, middle + 1, r));
}

int main(void) {
memset(t, 0, sizeof(t));
int n, m;
cin >> n >> m;
build(1, 1, n);
for(int i = 1; i <= n; i++) {
int tmp;
cin >> tmp;
change(1, i, tmp);
}
while(m--) {
int p, x, y;
cin >> p >> x >> y;
switch(p) {
case 1 :
change(1, x, y);
break;
case 2 :
cout << get_sum(1, x, y) << endl;
break;
case 3 :
cout << get_max(1, x, y) << endl;
break;
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: