题目:

输入

每个测试点(输入文件)有且仅有一组测试数据。

每组测试数据的第1行为一个整数N,意义如前文所述。

每组测试数据的第2行为N个整数,分别描述每种商品的重量,其中第i个整数表示标号为i的商品的重量Pi。

每组测试数据的第3行为一个整数Q,表示小Hi进行的操作数。

每组测试数据的第N+4~N+Q+3行,每行分别描述一次操作,每行的开头均为一个属于0或1的数字,分别表示该行描述一个询问和一次商品的价格的更改两种情况。对于第N+i+3行,如果该行描述一个询问,则接下来为两个整数Li, Ri,表示小Hi询问的一个区间[Li, Ri];如果该行描述一次商品的价格的更改,则接下来为三个整数Li,Ri,NewP,表示标号在区间[Li, Ri]的商品的价格全部修改为NewP。

对于100%的数据,满足N<=10^5,Q<=10^5, 1<=Li<=Ri<=N,1<=Pi<=N, 0<Pi, NewP<=10^4。

输出

对于每组测试数据,对于每个小Hi的询问,按照在输入中出现的顺序,各输出一行,表示查询的结果:标号在区间[Li, Ri]中的所有商品的价格之和。

说白了,就是更新一个区间的值,和询问一个区间的值的和

解法:

题目上说了线段树,那就肯定是线段树了。

我也是最近才学的,其实就是叶子结点是一个数字的值,向上的父节点就是它所包含的数字范围的和的值。

线段树快也主要是在查询时不用一个一个数字的加,而是遇到符合的区间后可以直接获取整个区间的和。

线段树的结点包括:左右范围、区间和的值、左右子树

typedef struct Node
{
int left, right; //区间左右值
int totalCharge; //区间总价格
Node *pLeft, *pRight; //子区间指针
}Node;

线段树的操作主要是查询和更新
用指针的线段树代码如下,测试结果超时。

#include <stdio.h>
#include <stdlib.h> int Num[] = {};
int sum;
typedef struct Node
{
int left, right; //区间左右值
int totalCharge; //区间总价格
Node *pLeft, *pRight; //子区间指针
}Node; Node * build(int l, int r) //建立线段树
{
Node * root = (Node*)malloc(sizeof(Node));
root->left = l;
root->right = r; if(l == r)
{
root->totalCharge = Num[l];
root->pLeft = NULL;
root->pRight = NULL;
}
else
{
int mid = (r + l) >> ;
root->pLeft = build(l, mid);
root->pRight = build(mid + , r);
root->totalCharge = root->pLeft->totalCharge + root->pRight->totalCharge;
} return root;
} void updateTree(Node * root, int l, int r, int newP)
{
if(root->left == root->right)
{
root->totalCharge = newP;
return;
}
int m = (root->left + root->right) >> ;
if(l > m) //都在右子树
{
updateTree(root->pRight, l, r, newP);
root->totalCharge = root->pLeft->totalCharge + root->pRight->totalCharge;
}
else if(r <= m) //都在左子树
{
updateTree(root->pLeft, l, r, newP);
root->totalCharge = root->pLeft->totalCharge + root->pRight->totalCharge;
}
else
{
updateTree(root->pLeft, l, m, newP);
updateTree(root->pRight, m + , r, newP);
root->totalCharge = root->pLeft->totalCharge + root->pRight->totalCharge;
}
} void getRangeNum(Node * root, int data1, int data2)
{
if(data1 == root->left && data2 == root->right) //区间恰好重合
{
sum += root->totalCharge;
return;
}
int m = (root->left + root->right) >> ; if(data1 > m) //都在右子树
{
getRangeNum(root->pRight, data1, data2);
}
else if(data2 <= m) //都在左子树
{
getRangeNum(root->pLeft, data1, data2);
}
else
{
getRangeNum(root->pLeft, data1, m);
getRangeNum(root->pRight, m + , data2);
}
} int main()
{
Node * root = NULL;
int count = ;
int N; //一共有多少组数据
int OperateNum = ;
int cmd;
scanf("%d", &N);
while(N--)
{
scanf("%d", Num + count++);
}
root = build(, count);
scanf("%d", &OperateNum); while(OperateNum--)
{
scanf("%d",&cmd);
if(cmd == ) //询问
{
int l, r;
scanf("%d %d", &l, &r);
sum = ;
getRangeNum(root, l - , r - );
printf("%d\n",sum);
}
else if(cmd == )//更改
{
int l, r, newP;
scanf("%d %d %d", &l, &r, &newP);
updateTree(root, l - , r - , newP);
}
} return ;
}

指针的超时了,想当然的建立了数组的线段树

typedef struct Node
{
int value;
int left, right;
}Node;
Node SegmentTree[ * maxind] = {};

我建立线段树是按根节点是0这样建立的,所以 node 的左子树为 2 * node + 1, 右子树为 2 * node + 2。而且查询时,如果问2-5,则应输入1-4,因为我这样是从0开始的,而题目中是从1开始的。

但网上很多是从1开始的,那样左子树就是2 * node, 右子树为 2 * node + 1,这里注意。

结果,居然又超时了!!

#include <stdio.h>
#include <stdlib.h>
const int maxind = ; typedef struct Node
{
int value;
int left, right;
}Node;
int Num[maxind] = {};
Node SegmentTree[ * maxind] = {};
int sum; void build(int node, int l, int r) //建立线段树
{
SegmentTree[node].left = l;
SegmentTree[node].right = r;
if(l == r)
{
SegmentTree[node].value = Num[l];
}
else
{
int m = (l + r) >> ;
build( * node + , l, m);
build( * node + , m + , r);
SegmentTree[node].value = SegmentTree[ * node + ].value + SegmentTree[ * node + ].value;
}
} void updateTree(int node, int l, int r, int newP)
{
if(l == SegmentTree[node].left && r == SegmentTree[node].right && l == r)
{
SegmentTree[node].value = newP;
return;
}
int m = (SegmentTree[node].left + SegmentTree[node].right) >> ;
if(l > m) //都在右子树
{
updateTree( * node + , l, r, newP);
SegmentTree[node].value = SegmentTree[ * node + ].value + SegmentTree[ * node + ].value;
}
else if(r <= m) //都在左子树
{
updateTree( * node + , l, r, newP);
SegmentTree[node].value = SegmentTree[ * node + ].value + SegmentTree[ * node + ].value;
}
else
{
updateTree( * node + , l, m, newP);
updateTree( * node + , m + , r, newP);
SegmentTree[node].value = SegmentTree[ * node + ].value + SegmentTree[ * node + ].value;
}
} void getRangeNum(int node, int data1, int data2)
{
if(data1 == SegmentTree[node].left && data2 == SegmentTree[node].right) //区间恰好重合
{
sum += SegmentTree[node].value;
return;
}
int m = (SegmentTree[node].left + SegmentTree[node].right) >> ; if(data1 > m) //都在右子树
{
getRangeNum( * node + , data1, data2);
}
else if(data2 <= m) //都在左子树
{
getRangeNum( * node + , data1, data2);
}
else
{
getRangeNum( * node + , data1, m);
getRangeNum( * node + , m + , data2);
}
} int main()
{
int count = ;
int N; //一共有多少组数据
int OperateNum = ;
int cmd;
scanf("%d", &N);
while(N--)
{
scanf("%d", Num + count++);
}
build(, , count - );
scanf("%d", &OperateNum); while(OperateNum--)
{
scanf("%d",&cmd);
if(cmd == ) //询问
{
int l, r;
scanf("%d %d", &l, &r);
sum = ;
getRangeNum(, l - , r - );
printf("%d\n",sum);
}
else if(cmd == )//更改
{
int l, r, newP;
scanf("%d %d %d", &l, &r, &newP);
updateTree(, l - , r - , newP);
}
} return ;
}

只好再去学习lazy思想,每次并不都更新到叶子结点,而是在吻合的一整段区间上做标记,等查询到更细的区间时再把子区间更新。

typedef struct Node
{
long long value;
long long lnc;
bool tag;
int left, right;
}Node;
Node SegmentTree[ * maxind] = {};

其中

tag 标记这个区间的数字是否是更新的,true为是,即整个区间的和是正确的,但它的子区间都没有更新
lnc 记录需要更新的区间,应该被更新的值是什么

下面给出更新和查询的伪代码,特别注意,区间获取值时间的一致性问题,所有的区间都是在获得tag标记的同时获得该区间正确的值的,如果这里不一致后面会出错。

还有,就是更新时也要做tag标签的向下调整,我就是在这里卡了好久。

更新的伪代码:

输入:根节点node,  更新区间 l,r  新值  newP

void update
{
  if(l,r区间与根节点区间完全吻合)
  {
    更新根节点的标记,更新值,区间和
  return;
  }   if(不满足上面条件,但是根节点tag=true) //即还有没向下更新的成分
  {
    向下传递更新的信息
    包括左右子树的标记,更新值,区间和 //注意,都是在区间获得tag标记的同时得到区间的范围值
  }
  根据数字范围,选择更新左右子树的部分
  获的该区间的和
}

查询的伪代码:

输入:根节点,查询区间 l, r
getRangeNum
{
  if(查询区间范围与根节点范围完全一致)
  {
    sum+=根节点值
    return;
  }
  if(不满足上面条件,但是根节点tag=true) //即还有没向下更新的成分
  {
    向下传递更新的信息
    包括左右子树的标记,更新值,区间和 //注意,都是在区间获得tag标记的同时得到区间的范围值
  }
  根据数字范围,选择查询左右子树的部分,递归查询
}

最后AC的代码如下: 话说AC的时候我感动的都要哭了.....

#include <stdio.h>
#include <stdlib.h>
const int maxind = ; typedef struct Node
{
long long value;
long long lnc;
bool tag;
int left, right;
}Node;
long long Num[maxind] = {};
Node SegmentTree[ * maxind] = {};
long long sum; void build(int node, int l, int r) //建立线段树
{
SegmentTree[node].left = l;
SegmentTree[node].right = r;
SegmentTree[node].lnc = ;
SegmentTree[node].tag = false;
if(l == r)
{
SegmentTree[node].value = Num[l];
}
else
{
int m = (l + r) >> ;
build( * node + , l, m);
build( * node + , m + , r);
SegmentTree[node].value = SegmentTree[ * node + ].value + SegmentTree[ * node + ].value;
}
} void updateTree(int node, int l, int r, int newP)
{
if(l == SegmentTree[node].left && r == SegmentTree[node].right)
{
SegmentTree[node].lnc = newP;
SegmentTree[node].value = newP * (r - l + );
SegmentTree[node].tag = true;
return;
} if(SegmentTree[node].tag == true) //注意 更新的时候也要把tag下移
{
SegmentTree[ * node + ].lnc = SegmentTree[node].lnc;
SegmentTree[ * node + ].lnc = SegmentTree[node].lnc;
SegmentTree[ * node + ].tag = true;
SegmentTree[ * node + ].tag = true;
SegmentTree[ * node + ].value = (SegmentTree[ * node + ].right - SegmentTree[ * node + ].left + ) * SegmentTree[ * node + ].lnc;
SegmentTree[ * node + ].value = (SegmentTree[ * node + ].right - SegmentTree[ * node + ].left + ) * SegmentTree[ * node + ].lnc;
SegmentTree[node].lnc = ;
SegmentTree[node].tag = false;
}
int m = (SegmentTree[node].left + SegmentTree[node].right) >> ;
if(l > m) //都在右子树
{
updateTree( * node + , l, r, newP);
}
else if(r <= m) //都在左子树
{
updateTree( * node + , l, r, newP);
}
else
{
updateTree( * node + , l, m, newP);
updateTree( * node + , m + , r, newP);
}
SegmentTree[node].value = SegmentTree[ * node + ].value + SegmentTree[ * node + ].value;
} void getRangeNum(int node, int data1, int data2)
{
if(data1 == SegmentTree[node].left && data2 == SegmentTree[node].right) //区间恰好重合
{
sum += SegmentTree[node].value;
return;
}
int m = (SegmentTree[node].left + SegmentTree[node].right) >> ;
if(SegmentTree[node].tag == true)
{
SegmentTree[ * node + ].lnc = SegmentTree[node].lnc;
SegmentTree[ * node + ].lnc = SegmentTree[node].lnc;
SegmentTree[ * node + ].tag = true;
SegmentTree[ * node + ].tag = true;
SegmentTree[ * node + ].value = (SegmentTree[ * node + ].right - SegmentTree[ * node + ].left + ) * SegmentTree[ * node + ].lnc;
SegmentTree[ * node + ].value = (SegmentTree[ * node + ].right - SegmentTree[ * node + ].left + ) * SegmentTree[ * node + ].lnc;
SegmentTree[node].lnc = ;
SegmentTree[node].tag = false;
} if(data1 > m) //都在右子树
{
getRangeNum( * node + , data1, data2);
}
else if(data2 <= m) //都在左子树
{
getRangeNum( * node + , data1, data2);
}
else
{
getRangeNum( * node + , data1, m);
getRangeNum( * node + , m + , data2);
}
} int main()
{
int count = ;
int N; //一共有多少组数据
int OperateNum = ;
int cmd;
scanf("%d", &N);
while(N--)
{
scanf("%d", Num + count++);
}
build(, , count - );
scanf("%d", &OperateNum); while(OperateNum--)
{
scanf("%d",&cmd);
if(cmd == ) //询问
{
int l, r;
scanf("%d %d", &l, &r);
sum = ;
getRangeNum(, l - , r - );
printf("%d\n",sum);
}
else if(cmd == )//更改
{
int l, r, newP;
scanf("%d %d %d", &l, &r, &newP);
updateTree(, l - , r - , newP);
}
} return ;
}

感叹一下,自己的编程能力着实是捉急啊...一共68个人提交,我排倒数......郁闷啊

【hihoCoder】第20周 线段树的更多相关文章

  1. hiho一下20周 线段树的区间修改

    线段树的区间修改 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 对于小Ho表现出的对线段树的理解,小Hi表示挺满意的,但是满意就够了么?于是小Hi将问题改了改,又出给了 ...

  2. hiho一下21周 线段树的区间修改 离散化

    离散化 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho在回国之后,重新过起了朝7晚5的学生生活,当然了,他们还是在一直学习着各种算法~ 这天小Hi和小Ho ...

  3. hihoCoder 1116 计算(线段树)

    http://hihocoder.com/problemset/problem/1116 题意: 思路: 用线段树解决,每个节点需要设置4个变量,sum记录答案,all记录整个区间的乘积,pre记录该 ...

  4. hihocoder #1299 : 打折机票 线段树

    #1299 : 打折机票 题目连接: http://hihocoder.com/problemset/problem/1299 Description 因为思念新宿的"小姐姐"们, ...

  5. hihoCoder 1586 Minimum 【线段树】 (ACM-ICPC国际大学生程序设计竞赛北京赛区(2017)网络赛)

    #1586 : Minimum 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 You are given a list of integers a0, a1, …, a2 ...

  6. hihoCoder:#1079(线段树+离散化)

    题目大意:给n个区间,有的区间可能覆盖掉其他区间,问没有完全被其他区间覆盖的区间有几个?区间依次给出,如果有两个区间完全一样,则视为后面的覆盖前面的. 题目分析:区间可能很长,所以要将其离散化.但离散 ...

  7. hihoCoder - 1079 - 离散化 (线段树 + 离散化)

    #1079 : 离散化 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描写叙述 小Hi和小Ho在回国之后,又一次过起了朝7晚5的学生生活.当然了.他们还是在一直学习着各种算法 ...

  8. UVA 11992 Fast Matrix Operations (二维线段树)

    解法:因为至多20行,所以至多建20棵线段树,每行建一个.具体实现如下,有些复杂,慢慢看吧. #include <iostream> #include <cstdio> #in ...

  9. codeforces 22E XOR on Segment 线段树

    题目链接: http://codeforces.com/problemset/problem/242/E E. XOR on Segment time limit per test 4 seconds ...

随机推荐

  1. MySQL的Order By Rand()的效率问题

    MySQL很多时候需要获取随机数据,举个例子,要从tablename表中随机提取一条记录,大家一般的写法就是: 但是,后来我查了一下MYSQL的官方手册,里面针对RAND()的提示大概意思就是,在OR ...

  2. 点击自动显示/隐藏DIV代码。(简单实用)

    注:本文由Colin撰写,版权所有!转载请注明原文地址,谢谢合作! 很多时候我们需要将DIV的信息默认为隐藏状态,只有当用户点击时才显示DIV中包含的提示文字.这类效果在互联网上应用得很多,但实现的方 ...

  3. [机器学习]信息&熵&信息增益

    关于对信息.熵.信息增益是信息论里的概念,是对数据处理的量化,这几个概念主要是在决策树里用到的概念,因为在利用特征来分类的时候会对特征选取顺序的选择,这几个概念比较抽象,我也花了好长时间去理解(自己认 ...

  4. POJ 2151 Check the difficulty of problems

    以前做过的题目了....补集+DP        Check the difficulty of problems Time Limit: 2000MS   Memory Limit: 65536K ...

  5. POJ 1496 Word Index

    组合数学....和上一题是一样的.... Word Index Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 4303 Acce ...

  6. CentOS6.3编译安装Memcached集群分布式缓存代理Magent-0.6出错汇总

    参考文章:Memcached集群/分布式/高可用 及 Magent缓存代理搭建过程 详解,搭建Magent,在编译的过程中会出现很多错误: #编译安装安装magent到 /usr/local/mage ...

  7. loadrunner 学习笔记--AJAX(转)

    用loadrunner测试WEB程序的时候总是会碰到AJAX或者ActiveX实现的功能,而通常这些功能会包含很多客户端函数(一般为JavaScript).我们该如何处理?如果从功能实现的角度去考虑这 ...

  8. javascript高级程序设计---Event对象三

    进度事件 进度事件用来描述一个事件进展的过程,比如XMLHttpRequest对象发出的HTTP请求的过程.<img>.<audio>.<video>.<st ...

  9. Redis Windows下安装部署

    下载Redis 在Redis的官网下载页上有各种各样的版本,我这次是在windows上部署的,要去GitHub上下载.我下载的是2.8.12版的,相信大家百度一下就可以搜到,这就是我们需要的: 启动R ...

  10. Markdown入门教程

    Markdown 是一种轻量级的「标记语言」,它的优点很多,目前也被越来越多的写作爱好者,撰稿者广泛使用.看到这里请不要被「标记」.「语言」所迷惑,Markdown 的语法十分简单.常用的标记符号也不 ...