您的位置:首页 > 其它

hdu1754 I hate it线段树模板 区间最值查询

2015-10-04 11:54 603 查看
题目链接:这道题是线段树,树状数组最基础的问题

两种分类方式:按照更新对象和查询对象

单点更新,区间查询;

区间更新,单点查询;

按照整体维护的对象:

维护前缀和;

维护区间最值.

线段树模板代码

#include<iostream>
#include<string.h>
#include<stdio.h>
#include<math.h>
#include<algorithm>
using namespace std;
#define re(i,n) for(int i=0;i<n;i++)
typedef long long ll;
const int maxn = 2 * 1e5 + 7;
#define lson(x) x<<1,f,mid
#define rson(x) x<<1|1,mid+1,t
int n, m;
int tr[maxn << 2];
int x, y;
int ans;
void build(int r, int f, int t){
if (f == t){
scanf("%d", &tr[r]);
return;
}
int mid = (f + t) >> 1;
build(lson(r)),build(rson(r));
tr[r] = max(tr[r << 1], tr[r << 1 | 1]);
}
void query(int r, int f, int t){
if (f >= x&&t <= y){
ans = max(ans, tr[r]);
return;
}
int mid = (f + t) >> 1;
if (x <= mid)query(lson(r));
if (y > mid)query(rson(r));
}
void update(int r, int f, int t){
if (f == t){
tr[r] = y;
return;
}
int mid = (f + t) >> 1;
if (x<= mid)update(lson(r));
else update(rson(r));
tr[r] = max(tr[r << 1], tr[r << 1 | 1]);
}
int main(){
//freopen("in.txt", "r", stdin);
while (cin >> n >> m){
build(1, 1, n);
while (m--){
char op[2];  scanf("%s%d%d", op,&x,&y);
if (op[0] == 'Q'){
if (x > y)swap(x, y);
ans = -1;
query(1, 1, n);
printf("%d\n", ans);
}
else{
update(1, 1, n);
}
}
}
return 0;
}


树状数组区间最值模板代码

#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn = 2*1e5+7;
typedef long long ll;
#define re(i,n) for(int i=0;i<n;i++)
int a[maxn], c[maxn];
/*
a里面存放本值,c里面维护最值
*/
int n, q;
int lowbit(int x){
return x&-x;
}
void redo(int i){
c[i] = i;
for (int j = 1; j < lowbit(i); j <<= 1)
if (a[c[i - j]]>a[c[i]])c[i] = c[i - j];
}
void init(){
for (int i = 1; i <= n; i++){
redo(i);
}
}
void update(int x, int y){
bool big = y > a[x];
a[x] = y;
int i = x;
if (big){
/*
这个地方如果错写成a[c[i]]<y就会导致无法传递上去,因为一开始时就是a[c[i]]==y.
*/
while (i<=n&&a[c[i]] <= y)c[i] = x, i += lowbit(i);
}
else{
while (i<=n&&c[i] == x)redo(i), i += lowbit(i);
}
}
int query(int l, int r){
int ans = a[r];
while (l<=r){
while (r - lowbit(r) >= l){
ans = max(a[c[r]], ans);
r -= lowbit(r);
}
ans = max(a[r], ans);//根节点
r--;//向下走一步
}
return ans;
}
int main(){
freopen("in.txt", "r", stdin);
while (scanf("%d%d", &n, &q) == 2){
re(i, n)scanf("%d", &a[i + 1]);
init();
int qi = 0;
while (q--){
char op[2]; int x, y;
qi++;
scanf("%s%d%d", &op, &x, &y);
if (op[0] == 'U'){
update(x, y);
}
else{
int ans = query(x, y);
printf("%d\n", ans);
}
}
}
return 0;
}


复杂度是O(lgn*lgn).这道题数据好像有点弱,因为错误的代码也能过这道题.

zwk线段树模板

zwk线段树巧妙之处在于开区间写法,对于长度为N的线段,初始化时,共N+2片叶子:1和N+2空着,2~N+1才是本体,是实实在在的数据.张昆玮说了:如果有1023个数据,那就要有2048片叶子.空间要开到(N+2)*4才行.虽然费了点空间,但编程简洁了.

根节点(1号结点)和每层的第一个节点和最后一个节点是哨兵节点,永远都不会访问到它们.它们的存在只是方便编程.老子曰:将欲取之,必先予之.一言以蔽之,就是两端边界处的节点都是哨兵节点,这是开区间方便编程导致的.

那么闭区间写法行不行呢?对于长度为N的线段,1~N全部用上,查询时闭区间可以转换成开区间.如果有1023个数,那就只需要开辟1024片叶子的树状数组.开区间=闭区间-两个端点.若无论如何都更新两个端点,那就会导致更新的东西有点靠下,这对于结果并没什么影响.需要注意l==r的情形.

zwk开区间写法

 #include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn = 2 * 1e5 + 7;
typedef long long ll;
#define re(i,n) for(int i=0;i<n;i++)
int a[maxn << 2];
int n, q, sz;
void update(int x, int v){
x += sz;
a[x] = v;
while (x >1){
x >>= 1;
a[x] = max(a[x << 1], a[x << 1 | 1]);
}
}
int query(int l, int r){
l += sz - 1, r += sz + 1;
int ans =0;
while (l^r ^ 1){//当两人不是兄弟时
if (~l & 1){
ans = max(ans, a[l ^ 1]);
}
if (r & 1){
ans = max(ans, a[r ^ 1]);
}
l >>= 1, r >>= 1;
}
return ans;
}
int main(){
//freopen("in.txt", "r", stdin);
while (scanf("%d%d", &n, &q) == 2){
sz = 1; while (sz < n+2)sz <<= 1;
re(i, n){
scanf("%d", &a[i + sz+1]);
}
for (int i = n; i <= (sz << 1); i++)a[i + sz+1] = 0;
for (int i = sz - 1; i>0; i--){
a[i] = max(a[i << 1], a[i << 1 | 1]);
}
while (q--){
char op[2]; int x, y;
scanf("%s%d%d", op, &x, &y);
if (op[0] == 'U'){
update(x, y);
}
else{
int ans = query(x, y);
printf("%d\n", ans);
}
}
}
return 0;
}


zwk闭区间写法:下面的代码有bug.能过题.

 #include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn = 2 * 1e5 + 7;
typedef long long ll;
#define re(i,n) for(int i=0;i<n;i++)
int a[maxn << 2];
int n, q, sz;
void update(int x, int v){
x += sz-1;
a[x] = v;
while (x >1){
x >>= 1;
a[x] = max(a[x << 1], a[x << 1 | 1]);
}
}
int query(int l, int r){
l += sz - 1, r += sz - 1;
int ans =max(a[l],a[r]); //查询时先把两个端点给处理掉,就相当于开区间了.
/*
这个地方的不同决定了两种写法,我觉得没有必要开区间.闭区间预处理一下就很好.

这个地方如果l==r,就会产生死循环.但是我这么写,这道题却过了.
*/
while (l^r ^ 1){//当两人不是兄弟时
if (~l & 1){
ans = max(ans, a[l ^ 1]);
}
if (r & 1){
ans = max(ans, a[r ^ 1]);
}
l >>= 1, r >>= 1;
}
return ans;
}
void init(){
sz = 1; while (sz < n)sz <<= 1;
re(i, n){
scanf("%d", &a[i + sz]);//在1~N之间存放数据
}
for (int i = n; i <= (sz << 1); i++)a[i + sz ] = 0;
for (int i = sz - 1; i>0; i--){
a[i] = max(a[i << 1], a[i << 1 | 1]);
}
}
int main(){
freopen("in.txt", "r", stdin);
while (scanf("%d%d", &n, &q) == 2){
init();
while (q--){
char op[2]; int x, y;
scanf("%s%d%d", op, &x, &y);
if (op[0] == 'U'){
update(x, y);
}
else{
int ans = query(x, y);
printf("%d\n", ans);
}
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: