Loading

LOJ 数列分块入门2

算法

考虑求小于给定值的数的个数,可以给每个块再维护一个已经排好序的数组,整块加法对于这个块排好序的数组没有影响,零散块加法直接在原序列上加,再将零散块所处的整块从新排序。查询时零散块暴力查找,整块二分查找

代码

#include <bits/stdc++.h>
#define int long long
const int MAXN = 1e6;

int n;
int Num[MAXN];

struct block
{
    int Left;
    int Right;
    int Size;
    int tmp[400];
    int cnt = 0;
    int add = 0;
} Blocks[400];
int Pos[MAXN];

bool cmp(int a, int b);

void Sort(int x)
{
    for (int i = Blocks[x].Left; i <= Blocks[x].Right; i++)
    {
        Blocks[x].tmp[++Blocks[x].cnt] = Num[i];
        // Pos[i] = x;
    }
    std::sort(Blocks[x].tmp + 1, Blocks[x].tmp + Blocks[x].Size + 1);
}

void Split()
{
    int Block_Size = sqrt(n);
    int Block_Num = n / Block_Size;
    if (n % Block_Size)
        Block_Num++;

    for (int i = 1; i < Block_Num; i++)
    {
        Blocks[i].Left = (i - 1) * Block_Size + 1;
        Blocks[i].Right = i * Block_Size;
        Blocks[i].Size = Block_Size;
        Sort(i);
    }
    Blocks[Block_Num].Left = Blocks[Block_Num - 1].Right + 1;
    Blocks[Block_Num].Right = n;
    Blocks[Block_Num].Size = Blocks[Block_Num].Right - Blocks[Block_Num].Left + 1;
    Sort(Block_Num);

    for (int i = 1; i <= n; i++)
    {
        Pos[i] = (i - 1) / Block_Size + 1;
    }
}

void Change(int Left, int Right, int Aim)
{
    int Left_Block = Pos[Left], Right_block = Pos[Right];

    if (Left_Block == Right_block)
    {
        for (int i = Left; i <= Right; i++)
        {
            Num[i] += Aim;
        }
        for (int i = Blocks[Left_Block].Left; i <= Blocks[Left_Block].Right; i++)
        {
            Blocks[Left_Block].tmp[i - Blocks[Left_Block].Left + 1] = Num[i];
        }
        std::sort(Blocks[Left_Block].tmp + 1, Blocks[Left_Block].tmp + Blocks[Left_Block].Size + 1);
    }
    else
    {
        for (int i = Left_Block + 1; i <= Right_block - 1; i++)
        {
            Blocks[i].add += Aim;
        }
        for (int i = Left; i <= Blocks[Left_Block].Right; i++)
        {
            Num[i] += Aim;
        }
        for (int i = Blocks[Left_Block].Left; i <= Blocks[Left_Block].Right; i++) //
        {
            Blocks[Left_Block].tmp[i - Blocks[Left_Block].Left + 1] = Num[i];
        }
        std::sort(Blocks[Left_Block].tmp + 1, Blocks[Left_Block].tmp + Blocks[Left_Block].Size + 1);

        for (int i = Blocks[Right_block].Left; i <= Right; i++)
        {
            Num[i] += Aim;
            Blocks[Right_block].tmp[i - Blocks[Right_block].Left + 1] = Num[i];
        }
        for (int i = Blocks[Right_block].Left; i <= Blocks[Right_block].Right; i++)
        {
            Blocks[Right_block].tmp[i - Blocks[Right_block].Left + 1] = Num[i];
        }
        std::sort(Blocks[Right_block].tmp + 1, Blocks[Right_block].tmp + Blocks[Right_block].Size + 1);
    }
}

int Find(int Left, int Right, int Aim)
{
    int Left_Block = Pos[Left], Right_block = Pos[Right];
    int Ans = 0;
    if (Left_Block == Right_block)
    {
        for (int i = Left; i <= Right; i++)
        {
            if (Num[i] + Blocks[Pos[i]].add < Aim)
                Ans++;
        }
    }

    else{
        for (int i = Left_Block + 1; i <= Right_block - 1; i++)
        {
            Ans += std::lower_bound(Blocks[i].tmp + 1, Blocks[i].tmp + Blocks[i].cnt + 1, Aim - Blocks[i].add) - (Blocks[i].tmp) - 1; //
        }
        for (int i = Left; i <= Blocks[Left_Block].Right; i++)
        {
            if (Num[i] + Blocks[Left_Block].add < Aim)
                Ans++;
        }

        for (int i = Blocks[Right_block].Left; i <= Right; i++)
        {
            if (Num[i] + Blocks[Right_block].add < Aim)
                Ans++;
        }
    }

    return Ans;
}

signed main()
{
    scanf("%lld", &n);
    for (int i = 1; i <= n; i++)
    {
        scanf("%lld", &Num[i]);
    }

    Split();

    for (int i = 1; i <= n; i++)
    {
        int op, l, r, c;
        scanf("%lld %lld %lld %lld", &op, &l, &r, &c);
        if (op == 0)
            Change(l, r, c);
        else
            printf("%lld\n", Find(l, r, c * c));
    }

    return 0;
}

bool cmp(int a, int b)
{
    return a < b;
}

/*
4
1 2 2 3
0 1 3 1
1 1 3 2
1 1 4 1
1 2 3 2

3
0
2
*/

总结

查找数列中 \(\geq\) 一个数, 可以用 std::lower_bound(a + 1, a + len + 1, Aim) - (a), 注意最后 (a) 不加 \(1\)

分块处理时注意零散块更改会影响随后整块的更改

后记

感谢 lhs

posted @ 2024-10-11 15:50  Yorg  阅读(17)  评论(0)    收藏  举报