首页 > 其他分享 >【知识】分块 & 块状列表

【知识】分块 & 块状列表

时间:2024-12-18 22:22:09浏览次数:7  
标签:cur 分块 int void ++ 列表 块状 include 节点

分块

将一个长度为 \(n\) 的序列分成 \(\sqrt n\) 段,那么每段长度不超过 \(\sqrt n\),每一个区间操作可以转化成 小于\(\sqrt n\) 个完整段 \(+\) \(2\)个长度小于 \(\sqrt n\) 的段。

时间复杂度:\(\mathcal{O}(n^2) \to \mathcal{O}(n \times \sqrt n )\)

模板:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cmath>

using namespace std;

typedef long long LL;
const int N = 100010, M = 350;

int n, m, len;
LL add[M], sum[M];
int w[N];

int get(int i)
{
    return i / len;
}

void change(int l, int r, int d)
{
    if (get(l) == get(r))  // 段内直接暴力
    {
        for (int i = l; i <= r; i ++ ) w[i] += d, sum[get(i)] += d;
    }
    else
    {
        int i = l, j = r;
        while (get(i) == get(l)) w[i] += d, sum[get(i)] += d, i ++ ;
        while (get(j) == get(r)) w[j] += d, sum[get(j)] += d, j -- ;
        for (int k = get(i); k <= get(j); k ++ ) sum[k] += len * d, add[k] += d;
    }
}

LL query(int l, int r)
{
    LL res = 0;
    if (get(l) == get(r))  // 段内直接暴力
    {
        for (int i = l; i <= r; i ++ ) res += w[i] + add[get(i)];
    }
    else
    {
        int i = l, j = r;
        while (get(i) == get(l)) res += w[i] + add[get(i)], i ++ ;
        while (get(j) == get(r)) res += w[j] + add[get(j)], j -- ;
        for (int k = get(i); k <= get(j); k ++ ) res += sum[k];
    }
    return res;
}

int main()
{
    scanf("%d%d", &n, &m);
    len = sqrt(n);
    for (int i = 1; i <= n; i ++ )
    {
        scanf("%d", &w[i]);
        sum[get(i)] += w[i];
    }

    char op[2];
    int l, r, d;
    while (m -- )
    {
        scanf("%s%d%d", op, &l, &r);
        if (*op == 'C')
        {
            scanf("%d", &d);
            change(l, r, d);
        }
        else printf("%lld\n", query(l, r));
    }

    return 0;
}

常用来做部分分,思路好想,代码好调!

块状链表

给每一个块加一个左右指针,分别指向前一个块和后一个块,插入删除操作同理。

为了保持时间复杂度严格 \(\mathcal{O}(n^2)\) 需要定期 merge 一下,把所有不完整的块合并。

P4008 [NOI2003] 文本编辑器

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 2000, M = 2010;

int n, x, y;
struct Node
{
    char s[N + 1];
    int c, l, r;
}p[M];
char str[2000010];
int q[M], tt;  // 内存回收

void move(int k)  // 移到第k个字符后面
{
    x = p[0].r;
    while (k > p[x].c) k -= p[x].c, x = p[x].r;
    y = k - 1;
}

void add(int x, int u)  // 将节点u插到节点x的右边
{
    p[u].r = p[x].r, p[p[u].r].l = u;
    p[x].r = u, p[u].l = x;
}

void del(int u)  // 删除节点u
{
    p[p[u].l].r = p[u].r;
    p[p[u].r].l = p[u].l;
    p[u].l = p[u].r = p[u].c = 0;  // 清空节点u
    q[ ++ tt] = u;  // 回收节点u
}

void insert(int k)  // 在光标后插入k个字符
{
    if (y < p[x].c - 1)  // 从光标处分裂
    {
        int u = q[tt -- ];  // 新建一个节点
        for (int i = y + 1; i < p[x].c; i ++ )
            p[u].s[p[u].c ++ ] = p[x].s[i];
        p[x].c = y + 1;
        add(x, u);
    }
    int cur = x;
    for (int i = 0; i < k;)
    {
        int u = q[tt -- ];  // 创建一个新的块
        while (p[u].c < N && i < k)
            p[u].s[p[u].c ++ ] = str[i ++ ];
        add(cur, u);
        cur = u;
    }
}

void remove(int k)  // 删除光标后的k个字符
{
    if (p[x].c - 1 - y >= k)  // 节点内删
    {
        for (int i = y + k + 1, j = y + 1; i < p[x].c; i ++, j ++ ) p[x].s[j] = p[x].s[i];
        p[x].c -= k;
    }
    else
    {
        k -= p[x].c - y - 1;  // 删除当前节点的剩余部分
        p[x].c = y + 1;
        while (p[x].r && k >= p[p[x].r].c)
        {
            int u = p[x].r;
            k -= p[u].c;
            del(u);
        }
        int u = p[x].r;  // 删除结尾节点的前半部分
        for (int i = 0, j = k; j < p[u].c; i ++, j ++ ) p[u].s[i] = p[u].s[j];
        p[u].c -= k;
    }
}

void get(int k)  // 返回从光标开始的k个字符
{
    if (p[x].c - 1 - y >= k)  // 节点内返回
    {
        for (int i = 0, j = y + 1; i < k; i ++, j ++ ) putchar(p[x].s[j]);
    }
    else
    {
        k -= p[x].c - y - 1;
        for (int i = y + 1; i < p[x].c; i ++ ) putchar(p[x].s[i]);  // 输出当前节点的剩余部分
        int cur = x;
        while (p[cur].r && k >= p[p[cur].r].c)
        {
            int u = p[cur].r;
            for (int i = 0; i < p[u].c; i ++ ) putchar(p[u].s[i]);
            k -= p[u].c;
            cur = u;
        }
        int u = p[cur].r;
        for (int i = 0; i < k; i ++ ) putchar(p[u].s[i]);
    }
    puts("");
}

void prev()  // 光标向前移动一位
{
    if (!y)
    {
        x = p[x].l;
        y = p[x].c - 1;
    }
    else y -- ;
}

void next()  // 光标向后移动一位
{
    if (y < p[x].c - 1) y ++ ;
    else
    {
        x = p[x].r;
        y = 0;
    }
}

void merge()  // 将长度较短的相邻节点合并,保证块状链表时间复杂度的核心
{
    for (int i = p[0].r; i; i = p[i].r)
    {
        while (p[i].r && p[i].c + p[p[i].r].c < N)
        {
            int r = p[i].r;
            for (int j = p[i].c, k = 0; k < p[r].c; j ++, k ++ )
                p[i].s[j] = p[r].s[k];
            if (x == r) x = i, y += p[i].c;  // 更新光标的位置
            p[i].c += p[r].c;
            del(r);
        }
    }
}

int main()
{
    for (int i = 1; i < M; i ++ ) q[ ++ tt] = i;
    scanf("%d", &n);
    char op[10];

    str[0] = '>';
    insert(1);  // 插入哨兵
    move(1);  // 将光标移动到哨兵后面

    while (n -- )
    {
        int a;
        scanf("%s", op);
        if (!strcmp(op, "Move"))
        {
            scanf("%d", &a);
            move(a + 1);
        }
        else if (!strcmp(op, "Insert"))
        {
            scanf("%d", &a);
            int i = 0, k = a;
            while (a)
            {
                str[i] = getchar();
                if (str[i] >= 32 && str[i] <= 126) i ++, a -- ;
            }
            insert(k);
            merge();
        }
        else if (!strcmp(op, "Delete"))
        {
            scanf("%d", &a);
            remove(a);
            merge();
        }
        else if (!strcmp(op, "Get"))
        {
            scanf("%d", &a);
            get(a);
        }
        else if (!strcmp(op, "Prev")) prev();
        else next();
    }

    return 0;
}

标签:cur,分块,int,void,++,列表,块状,include,节点
From: https://www.cnblogs.com/fanrunze/p/18615944

相关文章

  • 登入器列表内容
    [61TECH_HEIBAILIUYI]#gaIcRdcd5dAdFcyc7dfaRaAcZcmdWdldMdkc5cgarcgctdVdPcgdfdncdb8cldadBd3dmc2docEb7c4c8czcEcRdfeudrbzbtbCbxbtbUbUbTbWbIa0aXaKa1a0aNaJa5a1aVb5bWbTbNbJbKaGa2a0aWbVb5b8bJbcc0aHaWaOa1b7bFbldCcZcgeSd4dUdndedqcpdtcybaafbeeheYcDcddsdWcFcPb4dBcyc7d......
  • 写一个块状可以拖动的布局
    创建一个可拖动的块状布局,你可以使用HTML、CSS和JavaScript。以下是一个简单的示例,展示如何创建一个可拖动的<div>元素:HTML<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=devi......
  • 使用Python脚本之家商品列表实现的解析
    本文将详细介绍如何使用Python脚本之家商品列表来实现各种功能。通过对不同方面的阐述,帮助读者更好地理解和应用这个功能。一、创建商品列表1、首先,我们需要导入所需的库,如下所示:代码语言:javascript复制importrequestsfrombs4importBeautifulSoup2、接下来,我们可以使用......
  • Python中的列表,元组
    列表列表的特点:有序,可重复,长度可变(增删改查),异构,可切片,可遍历。列表的基本语法:列表名=[元素]list=['apple','banana','pineapple']列表的作⽤是⼀次性存储多个数据,并且列表可以存储不同类型的数据一:列表的增删改查:增加:append():增加指定数据到列表中names=['1',......
  • 在PbootCMS中如何隐藏没有缩略图的文章列表项?
    在PbootCMS中,如果你希望在文章列表中完全隐藏没有缩略图的文章列表项,可以通过PbootCMS提供的条件判断标签 pboot:if 来实现。以下是如何使用 pboot:if 标签来判断文章是否有缩略图,并在没有缩略图时隐藏整个列表项的详细步骤和示例代码:理解 isico 属性:在PbootCMS中,每篇文......
  • 零基础微信小程序开发——WXML 模板语法之列表渲染-WXML和样式设置(保姆级教程+超详细)
    ......
  • 如何在PbootCMS中使用搜索结果列表标签来显示文章的详细信息?
    在PbootCMS中,搜索结果列表标签提供了丰富的选项,可以用来显示文章的详细信息。这些标签可以帮助你构建一个详细且用户友好的搜索结果页面。以下是一些常用的搜索结果列表标签及其用法:[search:title]:显示文章的标题。[search:author]:显示文章的作者。[search:source]:显示文章的......
  • Python序列的应用(九):集合以及列表、元组、字典和集合的区别
    前言:在Python编程语言中,序列是一类非常重要的数据结构,它们提供了一种有序地存储和访问数据的方式。在Python中,序列的应用非常广泛,它们是处理数据集合的基础工具。本系列文章旨在深入探讨Python中的序列类型,包括列表(List)、元组(Tuple)、字典(Dictionary)和集合(Set),并分析它们之间的区别......
  • Redis应用—2.在列表数据里的应用
    大纲1.基于数据库+缓存双写的分享贴功能2.查询分享贴列表缓存时的延迟构建3.分页列表惰性缓存方案如何节约内存4.用户分享贴列表数据按页缓存实现精准过期控制5.用户分享贴列表的分页缓存的异步更新6.数据库与缓存的分页数据一致性方案7.热门用户分享贴列表的分页缓存失......
  • 零基础微信小程序开发——WXML 模板语法之列表渲染(保姆级教程+超详细)
    ......