首页 > 其他分享 >分块快速入门

分块快速入门

时间:2022-11-20 23:46:29浏览次数:30  
标签:入门 分块 int void pos include 快速

基本思想

有一句老话,叫“大段维护,局部朴素”。

其实就是将一些东西人为的分为若干块,然后每个块整体的维护一些东西,小范围内直接暴力做,做到时空平衡。

其实和根号分治有点像。

然后整除分块和莫队我觉得和传统分块差别有点大了,就没写。

数列分块我一般的写法是维护每个位置所在块编号,块的左端点和块的右端点。

数列分块入门题

LOJ 数列分块入门 1

区间加,单点查。

维护区间整体增加了多少,修改的时候如果覆盖了整块就加到整块增量里去,否则直接加到数上。

查询就返回数组里的数+所在块的总体增量。

时间复杂度 \(O(N\sqrt N)\)。

#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 50005;
int len = 250, n, a[N];
int pos[N], add[N], L[N], R[N];
void init() {
    for (int i = 1; i <= n; i++) pos[i] = (i - 1) / len + 1;
    for (int i = 1; i <= n; i++) {
        if (pos[i] != pos[i - 1]) L[i] = i;
        else L[i] = L[i - 1];
    }
    for (int i = n; i >= 1; i--) {
        if (pos[i] != pos[i + 1]) R[i] = i;
        else R[i] = R[i + 1];
    }
}
void upd(int l, int r, int v) {
    if (pos[l] == pos[r]) {
        for (int i = l; i <= r; i++) a[i] += v;
        return ;
    }
    int p = pos[l] + 1, q = pos[r] - 1;
    for (int i = p; i <= q; i++) add[i] += v;
    for (int i = l; i <= R[l]; i++) a[i] += v;
    for (int i = L[r]; i <= r; i++) a[i] += v;
}
int qry(int p) {
    return a[p] + add[pos[p]];
}
int main(void) {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%d", a + i);
    init();
    for (int i = 1, op, l, r, c; i <= n; i++) {
        scanf("%d%d%d%d", &op, &l, &r, &c);
        if (op == 0) {
            upd(l, r, c);
        } else {
            printf("%d\n", qry(r));
        }
    }
    return 0;
}

LOJ 数列分块入门 2

区间加,查区间某元素排名。

还是储存整块的增量,一个位置实际的值就是访问到的+所在块的增量。

然后每块暴力预处理排一下序,修改的时候零散的也排序。每次修改最多排两次。

查询的话,对于整块的 lower_bound 一下,零散的一个一个数。

时间复杂度 \(O(N \sqrt n \log n)\)。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 50005;
int len = 250, n, a[N], cnt;
int pos[N], add[N], L[N], R[N], rnk[N];
void init() {
    for (int i = 1; i <= n; i++) pos[i] = (i - 1) / len + 1;
    cnt = pos[n];
    for (int i = 1; i <= cnt; i++) L[i] = (i - 1) * len + 1, R[i] = min(n, i * len);
    memmove(rnk, a, sizeof(int) * n);
    for (int i = 1; i <= cnt; i++) sort(rnk + L[i], rnk + R[i] + 1);
}
void upd(int l, int r, int v) {
    int p = pos[l], q = pos[r];
    if (p == q) {
        for (int i = l; i <= r; i++) a[i] += v;
        memmove(rnk + L[p], a + L[p], sizeof(int) * (R[p] - L[p] + 1));
        sort(rnk + L[p], rnk + R[q] + 1);
        return ;
    }
    for (int i = p + 1; i <= q - 1; i++) add[i] += v;
    for (int i = l; i <= R[p]; i++) a[i] += v;
    for (int i = L[q]; i <= r; i++) a[i] += v;
    memmove(rnk + L[p], a + L[p], sizeof(int) * (R[p] - L[p] + 1));
    memmove(rnk + L[q], a + L[q], sizeof(int) * (R[q] - L[q] + 1));
    sort(rnk + L[p], rnk + R[p] + 1), sort(rnk + L[q], rnk + R[q] + 1);
}
int qry(int l, int r, int v) {
    int ret = 0, p = pos[l], q = pos[r];
    if (p == q) {
        for (int i = l; i <= r; i++) ret += ((a[i] + add[pos[i]]) < v);
        return ret;
    }
    for (int i = p + 1; i <= q - 1; i++) {
        ret += lower_bound(rnk + L[i], rnk + R[i] + 1, v - add[i]) - (rnk + L[i] - 1) - 1;
    }
    for (int i = l; i <= R[p]; i++) ret += ((a[i] + add[p]) < v);
    for (int i = L[q]; i <= r; i++) ret += ((a[i] + add[q]) < v);
    return ret;
}
int main(void) {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%d", a + i);
    init();
    for (int i = 1, op, l, r, c; i <= n; i++) {
        scanf("%d%d%d%d", &op, &l, &r, &c);
        if (op == 0) {
            upd(l, r, c);
        } else {
            printf("%d\n", qry(l, r, c * c));
        }
    }
    return 0;
}

LOJ 数列分块入门 3

区间加法,查区间前驱。

和前一道题一样,我们进行块内排序。

查询的时候 lower_bound 完以后,往回数一个就好了。

零散的还是一个个找。

时间复杂度 \(O(N\sqrt N \log N)\)。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5 + 5;
int len = 300, n, a[N], cnt;
int pos[N], add[N], L[N], R[N], rnk[N];
void init() {
    for (int i = 1; i <= n; i++) pos[i] = (i - 1) / len + 1;
    cnt = pos[n];
    for (int i = 1; i <= cnt; i++) L[i] = (i - 1) * len + 1, R[i] = min(n, i * len);
    memmove(rnk, a, sizeof(int) * n);
    for (int i = 1; i <= cnt; i++) sort(rnk + L[i], rnk + R[i] + 1);
}
void upd(int l, int r, int v) {
    int p = pos[l], q = pos[r];
    if (p == q) {
        for (int i = l; i <= r; i++) a[i] += v;
        memmove(rnk + L[p], a + L[p], sizeof(int) * (R[p] - L[p] + 1));
        sort(rnk + L[p], rnk + R[q] + 1);
        return ;
    }
    for (int i = p + 1; i <= q - 1; i++) add[i] += v;
    for (int i = l; i <= R[p]; i++) a[i] += v;
    for (int i = L[q]; i <= r; i++) a[i] += v;
    memmove(rnk + L[p], a + L[p], sizeof(int) * (R[p] - L[p] + 1));
    memmove(rnk + L[q], a + L[q], sizeof(int) * (R[q] - L[q] + 1));
    sort(rnk + L[p], rnk + R[p] + 1), sort(rnk + L[q], rnk + R[q] + 1);
}
int qry(int l, int r, int v) {
    int ret = -1, p = pos[l], q = pos[r];
    if (p == q) {
        for (int i = l; i <= r; i++) {
            if ((a[i] + add[p]) < v) {
                ret = max(ret, a[i] + add[p]);
            }
        }
        return ret;
    }
    for (int i = p + 1; i <= q - 1; i++) {
        int rkv = lower_bound(rnk + L[i], rnk + R[i] + 1, v - add[i]) - rnk;
        if (rkv != L[i]) ret = max(ret, rnk[rkv - 1] + add[i]);
    }
    for (int i = l; i <= R[p]; i++)
        if (a[i] + add[p] < v) ret = max(ret, a[i] + add[p]);
    for (int i = L[q]; i <= r; i++) 
        if (a[i] + add[q] < v) ret = max(ret, a[i] + add[q]);
    return ret;
}
int main(void) {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%d", a + i);
    init();
    for (int i = 1, op, l, r, c; i <= n; i++) {
        scanf("%d%d%d%d", &op, &l, &r, &c);
        if (op == 0) {
            upd(l, r, c);
        } else {
            printf("%d\n", qry(l, r, c));
        }
    }
    return 0;
}

LOJ 数列分块入门 4

经典的区间加,区间求和。

维护块内数的和、整体的增量,一个一个加的时候就直接加到和里面,整块的时候加到增量里面。

最后所求的和就是 维护的和 + 本块块长 * 区间增量。

和线段树很类似。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 5e4 + 5;
int len = 250, n, a[N], cnt;
int pos[N], L[N], R[N];
ll add[N], sum[N];
void init() {
    for (int i = 1; i <= n; i++) pos[i] = (i - 1) / len + 1;
    for (int i = 1; i <= n; i++) sum[pos[i]] += a[i];
    cnt = pos[n];
    for (int i = 1; i <= cnt; i++) L[i] = (i - 1) * len + 1, R[i] = min(n, i * len);
}
void upd(int l, int r, int v) {
    int p = pos[l], q = pos[r];
    if (p == q) {
        for (int i = l; i <= r; i++) a[i] += v, sum[p] += v;
        return ;
    }
    for (int i = p + 1; i <= q - 1; i++) add[i] += v;
    for (int i = l; i <= R[p]; i++) a[i] += v, sum[p] += v;
    for (int i = L[q]; i <= r; i++) a[i] += v, sum[q] += v;
}
int qry(int l, int r, int mod) {
    int p = pos[l], q = pos[r];
    ll ret = 0;
    if (p == q) {
        for (int i = l; i <= r; i++) {
            ret = (ret + a[i] + add[p]) % mod;
        }
        return ret;
    }
    for (int i = p + 1; i <= q - 1; i++) {
        ret = (ret + sum[i] + add[i] * (R[i] - L[i] + 1) % mod) % mod;
    }
    for (int i = l; i <= R[p]; i++)
        ret = (ret + a[i] + add[p]) % mod;
    for (int i = L[q]; i <= r; i++) 
        ret = (ret + a[i] + add[q]) % mod;
    return ret;
}
int main(void) {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%d", a + i);
    init();
    for (int i = 1, op, l, r, c; i <= n; i++) {
        scanf("%d%d%d%d", &op, &l, &r, &c);
        if (op == 0) {
            upd(l, r, c);
        } else {
            printf("%d\n", qry(l, r, c + 1));
        }
    }
    return 0;
}

块状链表

咕。

轻重链剖分(树剖)

咕。

标签:入门,分块,int,void,pos,include,快速
From: https://www.cnblogs.com/Lewis-Li/p/decompose.html

相关文章

  • Cortex-M入门
    Cortex-M入门还是要看书,看书才能系统性地掌握。手上得有块开发板,实践才能深刻理解。开发工具要用好,“工欲善其事,必先利其器”。还是要看书在网上看博客逛论坛也是能学到些东......
  • misc show入门wp(更新中)
    miscshow入门wp基础操作misc1图片下载下来就是misc2下载解压发现了一个txt文档打开是乱码于是用16进制编辑器查看一下发现是png文件于是更改文件后缀名保存下......
  • [排序算法] 快速排序 (C++) (含三种写法)
    快速排序解释快速排序QuickSort与归并排序一样,也是典型的分治法的应用。(如果有对归并排序还不了解的童鞋,可以看看这里哟~归并排序)❤❤❤快速排序的分治模式1、......
  • 线段树入门
    线段树是个好东西,首先要知道这些点:1.线段树适用于任何区间修改和区间查询的操作,复杂度只有O(logn)贼快2.线段树没有树状数组好写呢,其实也不难3.线段树每一个点是管理......
  • javascript入门
    javascript入门1.javascript的介绍JavaScript一种直译式脚本语言,是一种动态类型、弱类型、基于原型的语言,内置支持类型。它的解释器被称为JavaScript引擎,为浏览器的一部......
  • Linux性能工具-bpftrace入门
    一、bpftrace简介bpftrace是基于ebpf内核vm扩展出来的trace工具。bpftrace是Linux高级追踪工具和语言。该工具基于eBPF和BBC实现了通过探针机制采集内核和程序运......
  • ctfshow web入门部分题目 (更新中)
    CTFSHOW(WEB)web入门给她1参考文档https://blog.csdn.net/weixin_51412071/article/details/124270277查看链接sql注入<?php$pass=sprintf("andpass='%s'",addsla......
  • StarRocks入门
    StarRocks入门第1章StarRocks简介1.1StarRocks介绍StarRocks是新一代极速全场景MPP数据库StraRocks充分吸收关系型OLAP数据库和分布式存储系统在大数据时代的优秀研......
  • Canal 安装与入门
    MySQLBinlog简介MySQL主从复制过程1)Master主库将改变记录,写到二进制日志(BinaryLog)中;2)Slave从库向MySQLMaster发送dump协议,将Master主库的binarylogevents......
  • 用友NC产品接口开发,通过轻易云数据集成平台快速调用
    通过用友NC产品的UAPV63平台、插件相关处理、相关业务逻辑处理课程目标与要求课程内容课程目标与要求业务逻辑处理外部系统信息设置节点新建外部系统默认匹配规则:仅按对照......