分块详解

hzwer的9题
https://loj.ac/problem/6277
https://loj.ac/problem/6278
https://loj.ac/problem/6279
https://loj.ac/problem/6280
https://loj.ac/problem/6281
https://loj.ac/problem/6282
https://loj.ac/problem/6283
https://loj.ac/problem/6284
https://loj.ac/problem/6285

\(\text{分块是一种优雅的暴力}\)

\(\text{大概思想是这样的:维护sqrt(n)个块 查询的时候是查询区间内的块 如果有的部分不满一个块就用枚举 常数较小}\)

\(\text{个人觉得比线段树简单 思想比线段树容易但是代码长(小声}\)

\(\text{一般来讲的话 分块和线段树就差一个O2(因为线段树的常数大}\)

\(\text{分块的预处理是 将每一个数字存入一个块中 复杂度是O(N)}\)

\(\text{然后区间修改查询什么的 最多不超过}\) \(2 \ \sqrt(n)\)

\(\text{证明:一个块的大小是}\)\(\sqrt(n)\) \(\text{那么多出来的左区间和右区间的最坏情况是 ($\sqrt(n)$) 所以易证}\)

\(\text{分块的时间复杂度大概就是 }\) \(\theta (N + q * \sqrt(N))\)
(n指序列长度 q指查询修改的操作次数)
\(\text{以上就是一个基本的分块思想 简单讲 分块比线段树容易实现}\)

\(\text{分块1}\)
分块1是区间修改 单点查询

我们用一个数组维护块
如果区间修改的时候 包含这个块 那么可以把这个块加上需要修改的值 如果是多出来的就直接暴力修改了

然后查询的时候输出所在块修改的值 和 本身的值

扫描二维码关注公众号,回复: 7037323 查看本文章
// Isaunoya
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
using namespace std ;
const int N = 50000 + 5 ;
const int Bl = 300 + 5 ;
struct node {
    int l , r ;
    int add ;
} ;
int n ;
int a[N] ;
node atag[Bl] ;
int bl[N] ; int unt ;
inline void change(int l , int r , int c) {
    for(register int i = l ; i <= min(bl[l] * unt , r) ; i ++)
        a[i] += c ;
    if(bl[l] != bl[r])
        for(register int i = (bl[r] - 1) * unt + 1 ; i <= r ; i ++)
            a[i] += c ;
    for(register int i = bl[l] + 1 ; i <= bl[r] - 1 ; i ++)
        atag[i].add += c ;
}
signed main() {
    ios::sync_with_stdio(false) ;
    cin >> n ;
    for(register int i = 1 ; i <= n ; i ++) cin >> a[i] ;
    unt = sqrt(n) ;
    for(register int i = 1 ; i <= n ; i ++) {
        bl[i] = (i - 1) / unt + 1 ;
    }
    for(register int i = 1 ; i <= n ; i ++) {
        int opt ;
        cin >> opt ;
        if(opt == 0) {
            int l , r , c ;
            cin >> l >> r >> c ;
            change(l , r , c) ;
        }
        else {
            int l , r , c ;
            cin >> l >> r >> c ;
            cout << a[r] + atag[bl[r]].add << endl ;
        }
    }
    return 0 ;
}

\(\text{分块2}\)

分块二求的是区间修改 区间查询小于\(c^2\)的个数
这题用set维护一下(set内自动排序

#pragma GCC diagnostic error "-std=c++11"
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
inline int read() {
    register int x = 0;
    register int f = 1;
    register char c;
#define gc c = getchar()
    while (isspace(gc))
        ;
    c == '-' ? gc, f = -1 : 0;
    while (x = (x << 3) + (x << 1) + (c & 15), isdigit(gc))
        ;
    return x * f;
}
const int N = 100000 + 5;
const int Bl = 400 + 5;
int n;
int a[N];
struct node {
    int add;
};
node atag[Bl];
int unt;
int bl[N];
set<int> st[Bl];
inline void change(int l, int r, int c) {
    for (register int i = l ; i <= min(bl[l] * unt, r); i++) {
        st[bl[i]].erase(a[i]);
        a[i] += c;
        st[bl[i]].insert(a[i]);
    }
    if (bl[l] != bl[r]) {
        for (register int i = (bl[r] - 1) * unt + 1; i <= r; i++) {
            st[bl[i]].erase(a[i]);
            a[i] += c;
            st[bl[i]].insert(a[i]);
        }
    }
    for (register int i = bl[l] + 1; i <= bl[r] - 1; i++) atag[i].add += c;
}
inline int query(int l, int r, int c) {
    int ans = INT_MIN ;
    for (register int i = l; i <= min(bl[l] * unt, r); i++)
        if (a[i] + atag[bl[l]].add < c)
            ans = max(a[i] + atag[bl[l]].add, ans);
    if (bl[l] != bl[r]) {
        for (register int i = (bl[r] - 1) * unt + 1; i <= r; i++)
            if (a[i] + atag[bl[r]].add < c)
                ans = max(a[i] + atag[bl[r]].add, ans);
    }
    for (register int i = bl[l] + 1; i <= bl[r] - 1; i++) {
        int s = c - atag[i].add;
        auto find = st[i].lower_bound(s);
        if (find == st[i].begin()) continue ;
        find--;
        ans = max(ans, *find + atag[i].add);
    }
    return ans == INT_MIN ? -1 : ans ;
}
signed main() {
    n = read();
    unt = 500 + 5 ; 
    for (register int i = 1; i <= n; i++) a[i] = read();
    for (register int i = 1; i <= n; i++) bl[i] = (i - 1) / unt + 1;
    for (register int i = 1; i <= n; i++) {
        st[bl[i]].insert(a[i]);
    }
    for (register int i = 1; i <= n; i++) {
        int opt = read(), L = read(), R = read(), c = read();
        if (opt)
            printf("%d\n", query(L, R, c));
        else
            change(L, R, c);
    }
    return 0;
}

\(\text{分块3}\)

分块3 区间修改 询问区间内小于某个值 x的前驱即比x小的最大数

同样使用一个set维护
然后用\(lower _ \ bound\)二分
(部分C++11内容

// Isaunoya
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC diagnostic error "-std=c++11"
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
inline int read() {
    register int x = 0;
    register int f = 1;
    register char c;
#define gc c = getchar()
    while (isspace(gc))
        ;
    c == '-' ? gc, f = -1 : 0;
    while (x = (x << 3) + (x << 1) + (c & 15), isdigit(gc))
        ;
    return x * f;
}
const int N = 100000 + 5;
const int Bl = 400 + 5;
int n;
int a[N];
struct node {
    int add;
};
node atag[Bl];
int unt;
int bl[N];
set<int> st[Bl];
inline void change(int l, int r, int c) {
    for (register int i = l; i <= min(bl[l] * unt, r); i++) {
        st[bl[i]].erase(a[i]);
        a[i] += c;
        st[bl[i]].insert(a[i]);
    }
    if (bl[l] != bl[r]) {
        for (register int i = (bl[r] - 1) * unt + 1; i <= r; i++) {
            st[bl[i]].erase(a[i]);
            a[i] += c;
            st[bl[i]].insert(a[i]);
        }
    }
    for (register int i = bl[l] + 1; i <= bl[r] - 1; i++) atag[i].add += c;
}
inline int query(int l, int r, int c) {
    int ans = -1;
    for (register int i = l; i <= min(bl[l] * unt, r); i++)
        if (a[i] + atag[bl[l]].add < c)
            ans = max(a[i] + atag[bl[l]].add, ans);
    if (bl[l] != bl[r]) {
        for (register int i = (bl[r] - 1) * unt + 1; i <= r; i++)
            if (a[i] + atag[bl[r]].add < c)
                ans = max(a[i] + atag[bl[l]].add, ans);
    }
    for (register int i = bl[l] + 1; i <= bl[r] - 1; i++) {
        int s = c - atag[i].add;
        auto find = st[i].lower_bound(s);
        if (find == st[i].begin()) continue ;
        find--;
        ans = max(ans, *find + atag[i].add);
    }
    return ans;
}
signed main() {
    n = read();
    unt = sqrt(n);
    for (register int i = 1; i <= n; i++) a[i] = read();
    for (register int i = 1; i <= n; i++) bl[i] = (i - 1) / unt + 1;
    for (register int i = 1; i <= n; i++) {
        st[bl[i]].insert(a[i]);
    }
    for (register int i = 1; i <= n; i++) {
        int opt = read(), L = read(), R = read(), c = read();
        if (opt)
            printf("%d\n", query(L, R, c));
        else
            change(L, R, c);
    }
    return 0;
}

\({\text{分块4}}\)

分块4是区间修改 区间求和
然后 区间和 % \(c+1\)

我们考虑使用一个数组维护区间和 然后用一个数组维护整个块的修改情况

维护区间和指的是 暴力修改 的部分

// Isaunoya
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>

using namespace std ;
#define int long long
inline int read() { register int x = 0 ; register int f = 1 ; register char c ;
#define gc c = getchar()
    while(isspace(gc)) ;
    c == '-' ? gc , f = -1 : 0 ;
    while(x = (x << 1) + (x << 3) + (c ^ 48) , isdigit(gc)) ;
    return x * f ;
}

int n ;
const int N = 50000 + 5 ;
int a[N] ;
int bl[N] ;
int sum[N] ;
int atag[N] ;
int unt ;
inline void change(int l , int r , int c) {
    for(register int i = l ; i <= min(r , bl[l] * unt) ; i ++) {
        a[i] += c ;
        sum[bl[l]] += c ;
    }
    if(bl[l] != bl[r])
        for(register int i = (bl[r] - 1) * unt + 1 ; i <= r ; i ++) {
            a[i] += c ;
            sum[bl[r]] += c ;
        }
    for(register int i = bl[l] + 1 ; i <= bl[r] - 1 ; i ++) atag[i] += c ;
    return ;
}
inline int query(int l , int r , int c) { int ans = 0 ;
    for(register int i = l ; i <= min(r , bl[l] * unt) ; i ++) {
        ans += a[i] + atag[bl[l]] ;
        ans %= c ;
    }
    if(bl[l] != bl[r])
        for(register int i = (bl[r] - 1) * unt + 1 ; i <= r ; i ++) {
            ans += a[i] + atag[bl[r]] ;
            ans %= c ;
        }
    for(register int i = bl[l] + 1 ; i <= bl[r] - 1 ; i ++)
        ans = (ans + sum[i] + atag[i] * unt) % c ;
    return ans ;
}
signed main() {
    n = read() ; unt = sqrt(n) ;
    for(register int i = 1 ; i <= n ; i ++) a[i] = read() ;
    for(register int i = 1 ; i <= n ; i ++) bl[i] = (i - 1) / unt + 1 ;
    for(register int i = 1 ; i <= n ; i ++) {
        sum[bl[i]] += a[i] ;
    }
    for(register int i = 1 ;i <= n ; i ++) {
        int opt = read() ;
        if(opt == 0) {
            int l = read() , r = read() , c = read() ;
            change(l , r , c) ;
        }
        if(opt == 1) {
            int l = read() , r = read() , c = read() ;
            printf("%lld\n" , query(l , r , c + 1)) ;
        }
    }
    return 0 ;
}

猜你喜欢

转载自www.cnblogs.com/qf-breeze/p/11361618.html