【分块入门】LOJ 数列分块入门 1 - 9 (学习更新……)

dl题解 _「分块」数列分块入门1 – 9 by hzwer

LOJ #6277. 数列分块入门 1

  • 题意:给出一个长为n的数列,以及n个操作,操作涉及区间加法,单点查值。
  • 时间限制:100ms

分块

  • 我们将整个数列划分为很多块,暂且分为n / m块,用block[ i ]记录第 i 个数据arr[ i ]对应的块的个数。O(n)【数据读入时就可以操作完成,block[ i ] = (i - 1) / m + 1
  • 如果操作的区间涉及我们划分的“一整块”,那么我们可以直接用一个标记数组lazy[ ]来记录该“块”的add值。最大O(n / m)
  • 而操作区间两侧的“非完整的块”,至多有2m个元素,那么就直接暴力更新每个数据的值。最大O(m)
  • 所以区间操作的复杂度就是O(m) + O(n / m),根据均值不等式:a + b >= 2sqrt(ab),可以得到当m = sqrt(n)时,复杂度最优
  • 因为只有单点查询,所以某个点的答案就是arr[ i ] + lazy[ block[ i ] ]。O(1)
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
#define lowbit(x) x & (-x)

#define MID (l + r ) >> 1
#define lsn rt << 1
#define rsn rt << 1 | 1
#define Lson lsn, l, mid
#define Rson rsn, mid + 1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define eps  1e-6

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxN = 5e4 + 5;
int n, m, arr[maxN];
int block[maxN];
int lazy[maxN];
void add(int l, int r, int val)
{
    if(block[l] == block[r])//在一个块内,暴力
    {
        for(int i = l; i <= r; i ++ )
            arr[i] += val;
    }
    else
    {
        for(int i = l; i <= block[l] * m; i ++ )//非整块的左边,暴力
            arr[i] += val;
        for(int i = (block[r] - 1) * m + 1; i <= r; i ++ )//非整块的右边,暴力
            arr[i] += val;
        for(int i = block[l] + 1; i <= block[r] - 1; i ++ )//中间整块,标记数组记上
            lazy[i] += val;
    }
}
int main()
{
    scanf("%d", &n); m = sqrt(n);
    for(int i = 1; i <= n; i ++ )
    {
        scanf("%d", &arr[i]);
        block[i] = (i - 1) / m + 1;
    }
    for(int i = 1; i <= n; i ++ )
    {
        int op, l, r, c; scanf("%d%d%d%d", &op, &l, &r, &c);
        if(op == 0)//加
            add(l, r, c);
        else
            printf("%d\n", arr[r] + lazy[block[r]]);
    }
    return 0;
}

 

 LOJ #6278. 数列分块入门 2

  • 题意:给出一个长为 n 的数列,以及 n 个操作,操作涉及区间加法,询问区间内小于某个值 x 的元素个数。
  • 思路:500ms

分块

  • 同样的,我们将整个区间划分为\sqrt{n}个区间,每个区间有\sqrt{n}个元素。
  • 对于查询操作:因为我们要找区间内小于某个值x的元素,那我们就想了,如果每个块都是有序的,那么我们直接lower_bound就O(\sqrt{n}log\sqrt{n})找到“整块”中满足条件的个数。于是我们想到要给划分的块块内排序。所有块排序复杂度:O(\sqrt{n}\sqrt{n}log\sqrt{n})
  • 那么对于“非完整块”,我们还是直接暴力查找。O(\sqrt{n})
  • 对于修改操作:我们需要考虑,进行加的操作的时候,可以会改变“块”内的有序性。所以我们我们需要对这些“非完整块”进行重新排序
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
#define lowbit(x) x & (-x)

#define MID (l + r ) >> 1
#define lsn rt << 1
#define rsn rt << 1 | 1
#define Lson lsn, l, mid
#define Rson rsn, mid + 1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define eps  1e-6

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxN = 5e4 + 5;
inline int read()
{
    int x = 0, f = 1; char c = getchar();
    while(c < '0' || c > '9') { if(c == '-') f = -f; c = getchar(); }
    while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
    return x * f;
}
int n, m;
int arr[maxN], block[maxN], lazy[maxN];
vector<int>vt[maxN];
void reset(int x)
{
    vt[x].clear();
    for(int i = (x - 1) * m + 1; i <= x * m; i ++ )
        vt[x].push_back(arr[i]);
    sort(vt[x].begin(), vt[x].end());
}
void add(int l, int r, int val)
{
    if(block[l] == block[r])
    {
        for(int i = l; i <= r; i ++ )
            arr[i] += val;
        reset(block[l]);
    }
    else
    {
        for(int i = l; i <= block[l] * m; i ++ )
            arr[i] += val;
        reset(block[l]);
        for(int i = (block[r] - 1) * m + 1; i <= r; i ++ )
            arr[i] += val;
        reset(block[r]);
        for(int i = block[l] + 1; i <= block[r] - 1; i ++ )
            lazy[i] += val;
    }
}
int query(int l, int r, int val)
{
    int ans = 0;
    if(block[l] == block[r])
    {
        for(int i = l; i <= r; i ++ )
            if(arr[i] + lazy[block[i]] < val)
                ans ++;
    }
    else
    {
        for(int i = l; i <= block[l] * m; i ++ )
            if(arr[i] + lazy[block[i]] < val)
                ans ++;
        for(int i = (block[r] - 1) * m + 1; i <= r; i ++ )
            if(arr[i] + lazy[block[i]] < val)
                ans ++;
        for(int i = block[l] + 1; i <= block[r] - 1; i ++ )
            ans += lower_bound(vt[i].begin(), vt[i].end(), val - lazy[i]) - vt[i].begin();
    }
    return ans;
}
int main()
{
    n = read(); m =sqrt(n);
    for(int i = 1; i <= n; i ++ )
    {
        arr[i] = read();
        block[i] = (i - 1) / m + 1;
        vt[block[i]].push_back(arr[i]);
    }
    for(int i = 1; i <= block[n]; i ++ )
        sort(vt[i].begin(), vt[i].end());
    for(int i = 1; i <= n; i ++ )
    {
        int op, l, r, c; op = read(); l = read(); r = read(); c = read();
        if(op == 0) add(l, r, c);
        else printf("%d\n", query(l, r, c * c));
    }
    return 0;
}

 

发布了269 篇原创文章 · 获赞 73 · 访问量 2万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 技术黑板 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览