P3372 【模板】线段树 1(*ZKW1&分块1&伸展树1)
? 解题记录 ? ? 洛谷 ? ? 线段树 ? ? 分块 ? ? Splay ? ? 模板 ? ? 平衡树 ?    2017-10-24 19:21:49    770    0    0

题目描述

如题,已知一个数列,你需要进行下面两种操作:

1.将某区间每一个数加上x

2.求出某区间每一个数的和

输入输出格式

输入格式:

 

第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。

第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。

接下来M行每行包含3或4个整数,表示一个操作,具体如下:

操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k

操作2: 格式:2 x y 含义:输出区间[x,y]内每个数的和

 

输出格式:

 

输出包含若干行整数,即为所有操作2的结果。

 

输入输出样例

输入样例#1: 复制
5 5
1 5 4 2 3
2 2 4
1 2 3 2
2 3 4
1 1 5 1
2 1 4
输出样例#1: 复制
11
8
20

说明

时空限制:1000ms,128M

数据规模:

对于30%的数据:N<=8,M<=10

对于70%的数据:N<=1000,M<=10000

对于100%的数据:N<=100000,M<=100000

(数据已经过加强^_^,保证在int64/long long数据范围内)

样例说明:

ZKW的确是个好东西,就是写起来总是写不对,还需要多加练习啊。这是一道zkw写的板子,具体可以参考这一篇博客:=w= <----- 这篇博客。有几点需要注意:1、计算层数一只统计到<n + 3,因为有一个0,一个n + 1方便查询。2、pushdown比差分好写得多,常数也不大,可以考虑保留递归pushdown而不差分。 164ms:

#include<cstdio>
#define LL long long
using namespace std;
const int maxn = 1e6 + 5;
int data[maxn];
LL tree[maxn << 2], fre[maxn << 2];
int bit, n, m, o, a, dt, b, size[maxn << 2];
inline void push_up(const int &rt) {
    tree[rt] = tree[rt << 1 | 1] + tree[rt << 1];
}
inline void build(int n) {
    register int &b = bit;
    for(b = 1; b < n + 3; b <<= 1);
    for(register int i = 1; i <= n; ++i) tree[i + b] = data[i];
    for(register int i = b; i < (b << 1); ++i) size[i] = 1;	
    for(register int i = b - 1; i; --i) 
        size[i] = size[i << 1] + size[i << 1 | 1], push_up(i);
}
int len, lazy;
void push_down(int rt) {
    if(rt != 1) push_down(rt >> 1);
    if(!fre[rt]) return;
    len = size[rt << 1];
    lazy = fre[rt];
    tree[rt + rt + 1] += lazy * len;
    tree[rt << 1] += lazy * len;
    fre[rt << 1] += lazy;
    fre[rt + rt + 1] += lazy;
    fre[rt] = 0;
}
inline LL query(register int l, register int r) {
    LL ans = 0;
    l = bit + l - 1, r = bit + r + 1;
    push_down(l >> 1), push_down(r >> 1);
    for(; l ^ r ^ 1; l >>= 1, r >>= 1) {
        if(~l & 1) ans += tree[l ^ 1];
        if(r & 1) ans += tree[r ^ 1];
    }
    return ans;
}
inline void add(register int l, register int r, LL dt) {
    l = bit + l - 1, r = bit + r + 1;
    push_down(l >> 1), push_down(r >> 1);
    for(; l ^ r ^ 1; push_up(l >>= 1), push_up(r >>= 1)){
        if(~l & 1) fre[l ^ 1] += dt, tree[l ^ 1] += dt * size[l];
        if(r & 1) fre[r ^ 1] += dt, tree[r ^ 1] += dt * size[r];
    }
    for(l >>= 1; l; l >>= 1) push_up(l);
}
int main() {
    scanf("%d%d", &n, &m);
    for(register int i = 1; i <= n; ++i) scanf("%d", &data[i]);
    build(n);
    while(m--) {
        scanf("%d", &o);
        if(o == 1) scanf("%d%d%d", &a, &b, &dt), add(a, b, dt);
        else scanf("%d%d", &a, &b), printf("%lld\n", query(a, b));
    }
    return 0;
}

 突然发现分块也是一个好东西,要注意块的大小最好手动调整sqrt(n)不一定是最优的 824ms。

#include<cstdio>
#include<cmath>
#include<algorithm>
#define LL long long
using namespace std;
const int maxn = 5e5 + 5;
const int Bsize = 200;
LL a[maxn], sum[maxn], lazy[maxn], z;
int Bnum, n, m, op, x, y;

void push_down(int x) {
    if(!lazy[x]) return;
    int ed = min(n, Bsize * (x + 1)), st = Bsize * x;
    for(register int i = st; i < ed; ++i) 
        a[i] += lazy[x];
    lazy[x] = 0;
}

LL query(int l, int r) {
    LL ans = 0;
    --l, --r;
    int bl = l / Bsize, br = r / Bsize;
    if(bl == br) {
        push_down(bl);
        for(register int i = l; i <= r; ++i)
            ans += a[i];
        return ans;
    }
    for(register int i = bl + 1; i <= br - 1; ++i)
        ans += sum[i];
    push_down(bl), push_down(br);
    for(register int i = l; i <= (bl + 1) * Bsize - 1; ++i)
        ans += a[i];
    for(register int i = br * Bsize; i <= r; ++i)
        ans += a[i];
    return ans;
}

void add(int l, int r, LL w) {
    --l, --r;
    int bl = l / Bsize, br = r / Bsize;
    if(bl == br) {
        push_down(bl);
        for(register int i = l; i <= r; ++i)
            a[i] += w;
        sum[bl] += w * (r - l + 1);
        return ;
    }
    for(register int i = bl + 1; i <= br - 1; ++i)
        sum[i] += w * Bsize, lazy[i] += w;
    push_down(bl), push_down(br);
    for(register int i = l; i <= (bl + 1) * Bsize - 1; ++i)
        a[i] += w, sum[bl] += w;
    for(register int i = br * Bsize; i <= r; ++i)
        a[i] += w, sum[br] += w;
}

int main() {
    scanf("%d%d", &n, &m);
    for(register int i = 0; i < n; ++i) scanf("%lld", &a[i]);
    Bnum = (n - 1) / Bsize + 1;
    for(register int i = 0; i < Bnum; ++i) {
        int pre = i * Bsize;
        for(register int j = 0; j < Bsize; ++j)
            sum[i] += a[pre + j];
    }
    for(register int i = 1; i <= m; ++i) {
        scanf("%d", &op);
        if(op == 2) {
            scanf("%d%d", &x, &y);
            printf("%lld\n", query(x, y));
        } else {
            scanf("%d%d%lld", &x, &y, &z);
            add(x, y, z);
        }
    }
    return 0;
}

其实Splay也是一个好东西,虽然说它的log是假的,但是功能还是挺真的。1936ms:

#include<cstdio>
#include<cmath>
#include<algorithm>
#define LL long long
using namespace std;
const int maxn = 5e5 + 5;
const int Bsize = 200;
LL a[maxn], sum[maxn], lazy[maxn], z;
int Bnum, n, m, op, x, y;

void push_down(int x) {
    if(!lazy[x]) return;
    int ed = min(n, Bsize * (x + 1)), st = Bsize * x;
    for(register int i = st; i < ed; ++i) 
        a[i] += lazy[x];
    lazy[x] = 0;
}

LL query(int l, int r) {
    LL ans = 0;
    --l, --r;
    int bl = l / Bsize, br = r / Bsize;
    if(bl == br) {
        push_down(bl);
        for(register int i = l; i <= r; ++i)
            ans += a[i];
        return ans;
    }
    for(register int i = bl + 1; i <= br - 1; ++i)
        ans += sum[i];
    push_down(bl), push_down(br);
    for(register int i = l; i <= (bl + 1) * Bsize - 1; ++i)
        ans += a[i];
    for(register int i = br * Bsize; i <= r; ++i)
        ans += a[i];
    return ans;
}

void add(int l, int r, LL w) {
    --l, --r;
    int bl = l / Bsize, br = r / Bsize;
    if(bl == br) {
        push_down(bl);
        for(register int i = l; i <= r; ++i)
            a[i] += w;
        sum[bl] += w * (r - l + 1);
        return ;
    }
    for(register int i = bl + 1; i <= br - 1; ++i)
        sum[i] += w * Bsize, lazy[i] += w;
    push_down(bl), push_down(br);
    for(register int i = l; i <= (bl + 1) * Bsize - 1; ++i)
        a[i] += w, sum[bl] += w;
    for(register int i = br * Bsize; i <= r; ++i)
        a[i] += w, sum[br] += w;
}

int main() {
    scanf("%d%d", &n, &m);
    for(register int i = 0; i < n; ++i) scanf("%lld", &a[i]);
    Bnum = (n - 1) / Bsize + 1;
    for(register int i = 0; i < Bnum; ++i) {
        int pre = i * Bsize;
        for(register int j = 0; j < Bsize; ++j)
            sum[i] += a[pre + j];
    }
    for(register int i = 1; i <= m; ++i) {
        scanf("%d", &op);
        if(op == 2) {
            scanf("%d%d", &x, &y);
            printf("%lld\n", query(x, y));
        } else {
            scanf("%d%d%lld", &x, &y, &z);
            add(x, y, z);
        }
    }
    return 0;
}

 

上一篇: 洛谷P2320 [HNOI2006]鬼谷子的钱袋

下一篇: 洛谷P1007 独木桥

770 人读过
立即登录, 发表评论.
没有帐号? 立即注册
0 条评论
文档导航