POJ3468 A Simple Problem with Integers

来源:互联网 发布:linux 文件夹实时同步 编辑:程序博客网 时间:2024/06/05 19:38

题目大意:线段树区间加减,区间求和。

时间限制:5000ms

进行了位运算和输入优化,用时1469ms

分析:

build函数是建树,边界时把数据输到add数组里,add数组也就是常说的lazy标记,再把add的值赋给sum,建树完毕。

用sum[o]表示“如果只执行结点o及其子孙结点中的add操作,结点o对应区间中的所有数之和”,这样可以方便维护。

在执行update操作时,递归访问到的结点全部都要维护,并且是在递归返回后维护,因为这样子树的sum已经更新。

查询操作多加一个参数,表示当前区间的所有祖先结点add值之和,用全局变量sumv表示答案。

接下来是一些位运算优化,自行看代码。

数组开2.5倍区间长度会爆,开3倍不会。

数据类型用long long.

#include <iostream>#include <cstdio>using namespace std;typedef long long LL;char c;int n, q, x, y, z;LL sumv, sum[300005], add[300005];void build(int o, int L, int R) {if(L == R) {scanf("%lld", &add[o]);sum[o] = add[o];return;}int m = (L+R) >> 1;build(o<<1, L, m);build(o<<1 | 1, m+1, R);sum[o] = sum[o<<1] + sum[o<<1 | 1];}void maintain(int o, int L, int R) {sum[o] = 0;if(R > L) sum[o] = sum[o<<1] + sum[o<<1 | 1];sum[o] += add[o] * (R-L+1);}void update(int o, int L, int R) {if(x <= L && y >= R)add[o] += z;else {int m = (L+R) >> 1;if(x <= m) update(o<<1, L, m);if(y > m) update(o<<1 | 1, m+1, R);}maintain(o, L, R);}void query(int o, int L, int R, LL addv) {if(x <= L && y >= R)sumv += sum[o] + addv * (R-L+1);else {int m = (L+R) >> 1;if(x <= m) query(o<<1, L, m, addv+add[o]);if(y > m) query(o<<1 | 1, m+1, R, addv+add[o]);}}int main() {scanf("%d%d", &n, &q);build(1, 1, n);while(q--) {cin>>c;if(c == 'Q') {scanf("%d%d", &x, &y);sumv = 0;query(1, 1, n, 0);printf("%lld\n", sumv);}else {scanf("%d%d%d", &x, &y, &z);update(1, 1, n);}}return 0;}



3 0
原创粉丝点击