Poj-P3468题解【线段树】
本文为原创,转载请注明:http://www.cnblogs.com/kylewilson/
题目出处:
http://poj.org/problem?id=3468
题目描述:
给N个数A1, A2, ... , AN. 你需要处理两种操作,一种操作是在一个区间上每个数都增加一个数,别一种操作是查询一个区间所有数的和
输入
第一行两个数N, Q, 1 ≤ N,Q ≤ 100000
第二行N个数,数组初始值A1, A2, ... , AN. -1000000000 ≤ Ai ≤ 1000000000.
接着Q行,表示Q次操作"C a b c" 表示区间Aa, Aa+1, ... , Ab.,第个数增加c, -10000 ≤ c ≤ 10000.
"Q a b" 表示查询区间Aa, Aa+1, ... , Ab之和
10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4
思路分析:
首先分析数据规模,如果有简单模拟算法,时间复杂度O(N*Q),肯定会超时;
看题目需求,每次输入都是对区间进行增加或者查询等操作,所以也必须用区间操作算法来解决;
此文以线段树来解决,树状数组也很适合解决此类问题。事实证明能用树状数组解决的都可以用线段树来解决,反之则不行;
线段树维护区间信息,而树状数组维护前缀和信息;

如上图P1.1,对于增加操作如(a=0, b=2, c=1),需要对[0,2]区间每个点进行增加,但如果递归所有子树去增加操作,就退化成了线性;
所以遍历到[0,2]区间时,只对该点操作,并记录以该点以根的子树,应该增加的值,先记录,下次遍历到子树再执行操作

如上图P1.2,对[0,2]执行了操作,再子树还没有增加,如果下次查询子树则数据错误,所以每次在执行增加或者查询操作的时候,就把上次遗留下来的数据给补上,也就是所谓的Lazy思想。
每个树节点主要记录如下信息:
left: 区间左起点
right: 区间右终点
total: 区间和
childrenExtra: 子树还应该额外增加的值
lson, rson: 左右子树
注:因题目数据量大,输入输出用scanf, printf,如果用cin, cout则会超时。
C++源码如下:
github: https://github.com/Kyle-Wilson1/Poj/tree/master/P3468
#include <iostream>
#include <fstream>
#include <vector> using namespace std; struct SegmentTree {
long long left, right, total, childrenExtra;
SegmentTree *lson, *rson;
}; long long regionLength(SegmentTree *tree) {
return tree->right - tree->left + 1;
} void updateChildren(SegmentTree *root) {
if (root->lson != NULL && root->rson != NULL && root->childrenExtra != 0) {
root->lson->total += root->childrenExtra * regionLength(root->lson);
root->lson->childrenExtra += root->childrenExtra;
root->rson->total += root->childrenExtra * regionLength(root->rson);
root->rson->childrenExtra += root->childrenExtra;
root->childrenExtra = 0;
}
} SegmentTree *buildTree(vector<long long> &num, long long l, long long r) {
if (l > r) {
return NULL;
}
if (l == r) {
SegmentTree *root = new SegmentTree;
root->left = l;
root->right = r;
root->total = num[l];
root->childrenExtra = 0;
root->lson = NULL;
root->rson = NULL;
return root;
}
SegmentTree *root = new SegmentTree;
root->left = l;
root->right = r;
root->childrenExtra = 0;
long long mid = (l + r) >> 1;
root->lson = buildTree(num, l, mid);
root->rson = buildTree(num, mid + 1, r);
root->total = root->lson->total + root->rson->total;
return root;
} void addRegion(SegmentTree *root, long long regionLeft, long long regionRight, long long addNum) {
if (root == NULL || root->right < regionLeft || root->left > regionRight) {
return;
}
if (root->left >= regionLeft && root->right <= regionRight) {
root->total += addNum * regionLength(root);
if (root->left < root->right)root->childrenExtra += addNum;
return;
}
updateChildren(root);
addRegion(root->lson, regionLeft, regionRight, addNum);
addRegion(root->rson, regionLeft, regionRight, addNum);
root->total = root->lson->total + root->rson->total;
} void queryRegion(SegmentTree *root, long long regionLeft, long long regionRight, long long &sum) {
if (root == NULL || root->right < regionLeft || root->left > regionRight) {
return;
}
if (root->left >= regionLeft && root->right <= regionRight) {
sum += root->total;
return;
}
updateChildren(root);
queryRegion(root->lson, regionLeft, regionRight, sum);
queryRegion(root->rson, regionLeft, regionRight, sum);
}
int main() {
ifstream fin("a.in");
ofstream fout("a.out"); long long n, q, i, a, b, c;
char type[2];
SegmentTree *root; // scanf("%lld%lld", &n, &q);
fin >> n >> q; vector<long long> num(n, 0); for (i = 0; i < n; i++) {
// scanf("%lld", &num[i]);
fin >> num[i];
} n--;
root = buildTree(num, 0, n); for (i = 0; i < q; i++) {
// scanf("%s", type);
fin >> type;
if (type[0] == 'C') {
// scanf("%lld%lld%lld", &a, &b, &c);
fin >> a >> b >> c;
addRegion(root, a - 1, b - 1, c);
} else {
// scanf("%lld%lld", &a, &b);
fin >> a >> b;
long long sum = 0;
queryRegion(root, a - 1, b - 1, sum);
// printf("%lld\n", sum);
fout << sum << endl;
}
} return 0;
}
Poj-P3468题解【线段树】的更多相关文章
- POJ.2299 Ultra-QuickSort (线段树 单点更新 区间求和 逆序对 离散化)
POJ.2299 Ultra-QuickSort (线段树 单点更新 区间求和 逆序对 离散化) 题意分析 前置技能 线段树求逆序对 离散化 线段树求逆序对已经说过了,具体方法请看这里 离散化 有些数 ...
- POJ2182题解——线段树
POJ2182题解——线段树 2019-12-20 by juruoOIer 1.线段树简介(来源:百度百科) 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线 ...
- Buy Tickets POJ - 2828 思维+线段树
Buy Tickets POJ - 2828 思维+线段树 题意 是说有n个人买票,但是呢这n个人都会去插队,问最后的队列是什么情况.插队的输入是两个数,第一个是前面有多少人,第二个是这个人的编号,最 ...
- POJ 3468 A Simple Problem with Integers(详细题解) 线段树
这是个线段树题目,做之前必须要有些线段树基础才行不然你是很难理解的. 此题的难点就是在于你加的数要怎么加,加入你一直加到叶子节点的话,复杂度势必会很高的 具体思路 在增加时,如果要加的区间正好覆盖一个 ...
- poj City Horizon (线段树+二分离散)
http://poj.org/problem?id=3277 City Horizon Time Limit: 2000MS Memory Limit: 65536K Total Submissi ...
- poj 3667 Hotel (线段树)
http://poj.org/problem?id=3667 Hotel Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 94 ...
- poj 3225 【线段树】
poj 3225 这题是用线段树解决区间问题,看了两天多,算是理解一点了. Description LogLoader, Inc. is a company specialized in provid ...
- POJ 2352 Stars 线段树
题目链接 题意:在一个二维平面上有n个星星,每个星星的等级为x,x为该星星左方和下方所包含的星星的数量(包含正左和正下的),输出每个等级各有多少星星,星星坐标按照y序递增给出,y值相同按照x递增给出. ...
- poj 3264(线段树)
http://poj.org/problem?id=3264 初学线段可以做的水题,也是线段树的基础运用.也是我的第一个线段树的题. 题意:在区间范围内的最大值减去最小值 思路:线段树记录下每个区间内 ...
- POJ 3667 Hotel(线段树 区间合并)
Hotel 转载自:http://www.cnblogs.com/scau20110726/archive/2013/05/07/3065418.html [题目链接]Hotel [题目类型]线段树 ...
随机推荐
- 多图详解Go的互斥锁Mutex
转载请声明出处哦~,本篇文章发布于luozhiyun的博客:https://www.luozhiyun.com 本文使用的go的源码时14.4 Mutex介绍 Mutex 结构体包含两个字段: 字段s ...
- 百测学习之postman-接口测试
一.postman的请求 1.url与uri的区别 url与uri的区别 http://doc.nnzhp.cn/ http+host(域名)+path路径(uri) 2.g ...
- Django + FastDFS (分布式远程服务器存储文件)
之前随笔过一篇Docker来搭建分布式文件系统FastDfs就跳过了 https://www.cnblogs.com/xcsg/p/10901461.html FastDFS的Python (dja ...
- C#中的深度学习(四):使用Keras.NET识别硬币
在本文中,我们将研究一个卷积神经网络来解决硬币识别问题,并且我们将在Keras.NET中实现一个卷积神经网络. 在这里,我们将介绍卷积神经网络(CNN),并提出一个CNN的架构,我们将训练它来识别硬币 ...
- JDK8-日期时间新方式
日期时间新方式 在日常开发中,对于日期操作是非常常见的,但是对于有经验的开发人员来说Java8之前的日期操作是有较大问题 的.比方说SimpleDateFormat.但是在Java8之后提出了Da ...
- 机器学习 第4篇:数据预处理(sklearn 插补缺失值)
由于各种原因,现实世界中的许多数据集都包含缺失值,通常把缺失值编码为空白,NaN或其他占位符.但是,此类数据集与scikit-learn估计器不兼容,这是因为scikit-learn的估计器假定数组中 ...
- 80%的学校还在给新生上C语言,它们OUT了吗?
大家好,最近有小伙伴在后台问我,大一新生学校在教C语言,是不是已经过时了?第一门语言应该学什么比较好?大学期间什么课程对于毕业之后的从业帮助比较大呢? 今天这篇文章就和大家简单聊聊这个问题. 关于我 ...
- [LeetCode]319. Bulb Switcher灯泡开关
智商压制的一道题 这个题有个数学定理: 一般数(非完全平方数)的因子有偶数个 完全平凡数的因子有奇数个 开开关的时候,第i个灯每到它的因子一轮的时候就会拨动一下,也就是每个灯拨动的次数是它的因子数 而 ...
- [LeetCode]9. Palindrome Number判断回文数字
/* 查看网上的思路有两种: 1.每次取两边的数,然后进行比较 2.取数的倒置数,进行比较 */ public boolean isPalindrome1(int x) { if (x<0) r ...
- leetcode 274H-index
public int hIndex(int[] citations) { /* 唠唠叨叨说了很多 其实找到一个数h,使得数组中至少有h个数大于等于这个数, 其他N-h个数小于这个数,h可能有多个,求最 ...