码迷,mamicode.com
首页 > 其他好文 > 详细

[Loj] 数列分块

时间:2018-05-01 20:19:43      阅读:183      评论:0      收藏:0      [点我收藏+]

标签:out   min   ack   随机   while   tar   查找   names   mes   

数列分块入门 1

https://loj.ac/problem/6277

区间加 + 单点查询

#include <iostream>
#include <cstdio>
#include <cmath>

using namespace std;
const int N = 5e4 + 10;

#define gc getchar()

inline int read() {
    int x = 0; char c = gc;
    while(c < 0 || c > 9) c = gc;
    while(c >= 0 && c <= 9) x = x * 10 + c - 0, c = gc;
    return x;
}

int A[N], Add[N], bel[N];
int n, block, cnt;

void Sec_G(int x, int y, int w) {
    if(bel[x] == bel[y]) for(int i = x; i <= y; i ++) A[i] += w;
    else {
        for(int i = x; i <= bel[x] * block; i ++) A[i] += w;
        for(int i = (bel[y] - 1) * block + 1; i <= y; i ++) A[i] += w; 
    }
    for(int i = bel[x] + 1; i < bel[y]; i ++) Add[i] += w;
}

int main() {
    n = read();
    block = sqrt(n);
    for(int i = 1; i <= n; i ++) A[i] = read();
    for(int i = 1; i <= n; i ++) bel[i] = (i - 1) / block + 1;
    if(n % block) cnt = n / block + 1;
    else cnt = n / block;
    int T = n;
    while(T --) {
        int opt = read(), l = read(), r = read(), c = read();
        if(!opt) Sec_G(l, r, c);
        else cout << A[r] + Add[bel[r]] << endl;
    }
    return 0;
}

数列分块入门 2

https://loj.ac/problem/6278

区间加法,询问区间内小于某个值 x 的元素个数

用B[]记录A[], B[]数组中为排好序的A[]的映射

那么每次可以对每一块进行二分查找

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

using namespace std;
const int N = 5e4 + 10;

#define gc getchar()

inline int read() {
    int x = 0; char c = gc;
    while(c < 0 || c > 9) c = gc;
    while(c >= 0 && c <= 9) x = x * 10 + c - 0, c = gc;
    return x;
}

int A[N], B[N], Add[N], bel[N];
int n, block, cnt;

void Work_sort(int x) {
    int l = (x - 1) * block + 1, r = min(l + block - 1, n);
    for(int i = l; i <= r; i ++) B[i] = A[i];
    sort(B + l, B + r + 1);
}

void Sec_G(int x, int y, int w) {
    if(bel[x] == bel[y]) {
        for(int i = x; i <= y; i ++) A[i] += w;
        Work_sort(bel[x]);
    }
    else {
        for(int i = x; i <= bel[x] * block; i ++) A[i] += w; Work_sort(bel[x]);
        for(int i = (bel[y] - 1) * block + 1; i <= y; i ++) A[i] += w; Work_sort(bel[y]);
    }
    for(int i = bel[x] + 1; i < bel[y]; i ++) Add[i] += w;
}

inline int Calc(int x, int w) {
    int l = (x - 1) * block + 1, r = min(l + block - 1, n), ret = 0;
    while(l <= r) {
        int mid = (l + r) >> 1;
        if(B[mid] + Add[x] < w) ret = mid, l = mid + 1;
        else r = mid - 1;
    }
    return ret ? (ret - (x - 1) * block) : 0;
}

inline int Sec_A(int x, int y, int w) {
    int ret(0);
    if(bel[x] == bel[y]) {
        for(int i = x; i <= y; i ++) if(A[i] + Add[bel[x]] < w) ret ++;
        return ret;
    } else {
        for(int i = x; i <= bel[x] * block; i ++) if(A[i] + Add[bel[x]] < w) ret ++;
        for(int i = (bel[y] - 1) * block + 1; i <= y; i ++) 
        if(A[i] + Add[bel[y]] < w) 
        ret ++;
    }
    for(int i = bel[x] + 1; i < bel[y]; i ++) 
    ret += Calc(i, w);
    return ret;
}

int main() {
    n = read();
    block = sqrt(n);
    for(int i = 1; i <= n; i ++) A[i] = read();
    for(int i = 1; i <= n; i ++) bel[i] = (i - 1) / block + 1;
    if(n % block) cnt = n / block + 1;
    else cnt = n / block;
    for(int i = 1; i <= cnt; i ++) Work_sort(i);
    int T = n;
    while(T --) {
        int opt = read(), l = read(), r = read(), c = read();
        if(!opt) Sec_G(l, r, c);
        else cout << Sec_A(l, r, c * c) << "\n";
    }
    return 0;
}

数列分块入门 3

https://loj.ac/problem/6279

区间加法,询问区间内小于某个值 x 的前驱

与2类似,二分查找

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

using namespace std;
const int N = 1e5 + 10;
const int oo = 999999999;

#define gc getchar()

inline int read() {
    int x = 0; char c = gc;
    while(c < 0 || c > 9) c = gc;
    while(c >= 0 && c <= 9) x = x * 10 + c - 0, c = gc;
    return x;
}

int A[N], B[N] = {-1}, Add[N], bel[N];
int n, block, cnt;

void Work_sort(int x) {
    int l = (x - 1) * block + 1, r = min(l + block - 1, n);
    for(int i = l; i <= r; i ++) B[i] = A[i];
    sort(B + l, B + r + 1);
}

void Sec_G(int x, int y, int w) {
    if(bel[x] == bel[y]) {
        for(int i = x; i <= y; i ++) A[i] += w;
        Work_sort(bel[x]);
    }
    else {
        for(int i = x; i <= bel[x] * block; i ++) A[i] += w; Work_sort(bel[x]);
        for(int i = (bel[y] - 1) * block + 1; i <= y; i ++) A[i] += w; Work_sort(bel[y]);
    }
    for(int i = bel[x] + 1; i < bel[y]; i ++) Add[i] += w;
}

inline int Calc(int x, int w) {
    int l = (x - 1) * block + 1, r = min(l + block - 1, n), ret = 0;
    while(l <= r) {
        int mid = (l + r) >> 1;
        if(B[mid] + Add[x] < w) ret = mid, l = mid + 1;
        else r = mid - 1;
    }
    return B[ret] + Add[x];
}

inline int Sec_A(int x, int y, int w) {
    int ret = -1;
    if(bel[x] == bel[y]) {
        for(int i = x; i <= y; i ++) if(A[i] + Add[bel[x]] < w && A[i] + Add[bel[x]] > ret) ret = A[i] + Add[bel[x]];
        return ret;
    } else {
        for(int i = x; i <= bel[x] * block; i ++) 
        if(A[i] + Add[bel[x]] < w && A[i] + Add[bel[x]] > ret) ret = A[i] + Add[bel[x]];
        for(int i = (bel[y] - 1) * block + 1; i <= y; i ++) 
        if(A[i] + Add[bel[y]] < w && A[i] + Add[bel[y]] > ret) ret = A[i] + Add[bel[y]]; 
    }
    for(int i = bel[x] + 1; i < bel[y]; i ++) {
        int imp = Calc(i, w);
        if(imp < w && imp > ret) ret = imp;
    }
    return ret;
}

int main() {
    n = read();
    block = sqrt(n);
    for(int i = 1; i <= n; i ++) A[i] = read();
    for(int i = 1; i <= n; i ++) bel[i] = (i - 1) / block + 1;
    if(n % block) cnt = n / block + 1;
    else cnt = n / block;
    for(int i = 1; i <= cnt; i ++) Work_sort(i);
    int T = n;
    while(T --) {
        int opt = read(), l = read(), r = read(), c = read();
        if(!opt) Sec_G(l, r, c);
        else cout << Sec_A(l, r, c) << "\n";
    }
    return 0;
}

数列分块入门 4

区间加法,区间求和

https://loj.ac/problem/6280

没什么好说的

#include <iostream>
#include <cstdio>
#include <cmath>

using namespace std;
const int N = 5e4 + 10;

#define gc getchar()

inline int read() {
    int x = 0; char c = gc;
    while(c < 0 || c > 9) c = gc;
    while(c >= 0 && c <= 9) x = x * 10 + c - 0, c = gc;
    return x;
}

#define LL long long

LL A[N], Add[N], bel[N], W[N];
int n, block, cnt, Mod;

void Sec_G(int x, int y, int w) {
    if(bel[x] == bel[y]) for(int i = x; i <= y; i ++) A[i] += w, W[bel[x]] += w;
    else {
        for(int i = x; i <= bel[x] * block; i ++) A[i] += w, W[bel[x]] += w;
        for(int i = (bel[y] - 1) * block + 1; i <= y; i ++) A[i] += w, W[bel[y]] += w;
    }
    for(int i = bel[x] + 1; i < bel[y]; i ++) Add[i] += w, W[i] += w * block;
}

inline int Sec_A(int x, int y) {
    LL ret = 0;
    if(bel[x] == bel[y]) 
        for(int i = x; i <= y; i ++)
            ret += (A[i] + Add[bel[x]]) % Mod;
    else {
        for(int i = x; i <= bel[x] * block; i ++) ret += (A[i] + Add[bel[x]]) % Mod;
        for(int i = (bel[y] - 1) * block + 1; i <= y; i ++) ret += (A[i] + Add[bel[y]]) % Mod;
    }
    for(int i = bel[x] + 1; i < bel[y]; i ++) ret += W[i] % Mod;
    return ret% Mod;
}

int main() {
    n = read();
    block = sqrt(n);
    for(int i = 1; i <= n; i ++) A[i] = read();
    for(int i = 1; i <= n; i ++) bel[i] = (i - 1) / block + 1, W[bel[i]] += A[i];
    if(n % block) cnt = n / block + 1;
    else cnt = n / block;
    int T = n;
    while(T --) {
        int opt = read(), l = read(), r = read(), c = read(); Mod = c + 1;
        if(!opt) Sec_G(l, r, c);
        else cout << Sec_A(l, r) << endl;
    }
    return 0;
}

数列分块入门 5

区间开方,区间求和

一个数(合理)开几次根后就是0/1了

因此,只需记录每块的最大值,如果最大值是0/1就没必要开根

#include <iostream>
#include <cstdio>
#include <cmath>

using namespace std;
const int N = 5e4 + 10;

#define gc getchar()

inline int read() {
    int x = 0; char c = gc;
    while(c < 0 || c > 9) c = gc;
    while(c >= 0 && c <= 9) x = x * 10 + c - 0, c = gc;
    return x;
}

#define LL long long

LL A[N], Add[N], bel[N], W[N], Max[N];
int n, block, cnt, Mod;

inline void Sec_G(int x, int y) {
    if(bel[x] == bel[y]) {
        if(!Max[bel[x]]) return ;
        if(Max[bel[x]] == 1) return ;
        for(int i = x; i <= y; i ++) {
            int C = A[i] - (int) sqrt(A[i]);
            W[bel[x]] -= C; A[i] = (int) sqrt(A[i]);
        }
        LL Max_A = 0;
        for(int i = (bel[x] - 1) * block + 1; i <= bel[x] * block; i ++) Max_A = max(Max_A, A[i]);
        Max[bel[x]] = Max_A;
        return ;
    }
    else {
        if(Max[bel[x]] && Max[bel[x]] != 1) {
            for(int i = x; i <= bel[x] * block; i ++) {
                int C = A[i] - (int) sqrt(A[i]);
                W[bel[x]] -= C;
                A[i] = (int) sqrt(A[i]);
            }
            LL Max_A = 0;
            for(int i = (bel[x] - 1) * block + 1; i <= bel[x] * block; i ++) Max_A = max(Max_A, A[i]);
            Max[bel[x]] = Max_A;
        }
        if(Max[bel[y]] && Max[bel[y]] != 1) {
            for(int i = (bel[y] - 1) * block + 1; i <= y; i ++) {
                int C = A[i] - (int) sqrt(A[i]);
                W[bel[y]] -= C;
                A[i] = (int) sqrt(A[i]);
            }
            LL Max_A = 0;
            for(int i = (bel[y] - 1) * block + 1; i <= bel[y] * block; i ++) Max_A = max(Max_A, A[i]);
            Max[bel[y]] = Max_A;
        }    
    }
    for(int i = bel[x] + 1; i < bel[y]; i ++) {
        if(!Max[i] || Max[i] == 1) continue ;
        LL Max_A = 0;
        for(int j = (i - 1) * block + 1; j <= i * block; j ++) {
            int C = A[j] - (int) sqrt(A[j]);
            W[i] -= C;
            A[j] = (int) sqrt(A[j]);
            Max_A = max(Max_A, A[j]);
        }
        Max[i] = Max_A;
    }
}

inline int Sec_A(int x, int y) {
    LL ret = 0;
    if(bel[x] == bel[y] && Max[bel[x]]) for(int i = x; i <= y; i ++) ret += A[i];
    else {
        for(int i = x; i <= bel[x] * block && Max[bel[x]]; i ++) ret += A[i];
        for(int i = (bel[y] - 1) * block + 1; i <= y && Max[bel[y]]; i ++) ret += A[i];
    }
    for(int i = bel[x] + 1; i < bel[y]; i ++) ret += W[i];
    return ret;
}

int main() {
    n = read();
    block = sqrt(n);
    for(int i = 1; i <= n; i ++) A[i] = read();
    for(int i = 1; i <= n; i ++) bel[i] = (i - 1) / block + 1, W[bel[i]] += A[i], Max[bel[i]] = max(Max[bel[i]], A[i]);
    int T = n;
    while(T --) {
        int opt = read(), l = read(), r = read(), c = read();
        if(!opt) Sec_G(l, r);
        else cout << Sec_A(l, r) << "\n";
    }
    return 0;
}

数列分块入门 6

https://loj.ac/problem/6282

单点插入,单点询问

数据随机,分块,对于每一块开动态数组,插入 + 查询比较容易实现

如果数据不随机,就有可能加到同一块中的数较多,影响效率

这样可以进行一定的插入操作之后重新分块

#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>

using namespace std;
const int N = 1e5 + 10;

int A[N << 1], n;
vector <int> Vec[350];
int block, bel[N];

#define gc getchar()

inline int read() {
    int x = 0; char c = gc;
    while(c < 0 || c > 9) c = gc;
    while(c >= 0 && c <= 9) x = x * 10 + c - 0, c = gc;
    return x;
}

inline void Ins(int x, int a) {
    int now_size(0), Whi;
    for(int i = 1; ; i ++) {
        int Size = Vec[i].size();
        now_size += Size;
        if(now_size >= x) {
            Whi = i;
            x -= (now_size - Size);
            break;
        }
    }
    Vec[Whi].insert(Vec[Whi].begin() + x - 1, a);
}

inline int Poi_A(int x) {
    int Whi_, now_size(0);
    for(int i = 1; ; i ++) {
        int Size = Vec[i].size();
        now_size += Size;
        if(now_size >= x) {
            int iii = x - (now_size - Size);
            return Vec[i][iii - 1];
        }
    }
}

int main() {
    n = read();
    block = sqrt(n);
    for(int i = 1; i <= n; i ++) A[i] = read();
    for(int i = 1; i <= n; i ++) bel[i] = (i - 1) / block + 1;
    for(int i = 1; i <= n; i ++) Vec[bel[i]].push_back(A[i]);
    int T = n;

    while(T --) {
        int opt = read(), l = read(), r = read(), c = read();
        if(!opt) Ins(l, r);
        else cout << Poi_A(r) << endl;
    }
    return 0;
}

数列分块入门 7

https://loj.ac/problem/6283

区间乘法,区间加法,单点询问

先乘后加,乘的时候相应的加法标记也要乘

#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>

using namespace std;
const int N = 1e5 + 10;
const int Mod = 1e4 + 7;

#define LL long long

LL A[N], Mul[N], Add[N], bel[N];
int n, cnt, block;

#define gc getchar()

inline int read() {
    int x = 0, f = 1; char c = gc;
    while(c < 0 || c > 9) {if(c == -) f = -1; c = gc;}
    while(c >= 0 && c <= 9) x = x * 10 + c - 0, c = gc;
    return x;
}

inline void Sec_Add(int x, int y, int w) {
    if(bel[x] == bel[y]) {
        for(int i = (bel[x] - 1) * block + 1; i <= bel[x] * block; i ++) A[i] = (A[i] * Mul[bel[x]] + Add[bel[x]]);
        Add[bel[x]] = 0; Mul[bel[x]] = 1;
        for(int i = x; i <= y; i ++) A[i] += w, A[i] %= Mod;
        return ;
    } else {
        for(int i = (bel[x] - 1) * block + 1; i <= bel[x] * block; i ++) A[i] = (A[i] * Mul[bel[i]] + Add[bel[i]]) % Mod;
        Add[bel[x]] = 0; Mul[bel[x]] = 1;
        for(int i = x; i <= bel[x] * block; i ++) A[i] += w, A[i] %= Mod;
        for(int i = (bel[y] - 1) * block + 1; i <= bel[y] * block; i ++) A[i] = (A[i] * Mul[bel[i]] + Add[bel[i]]) % Mod;
        Add[bel[y]] = 0; Mul[bel[y]] = 1;
        for(int i = (bel[y] - 1) * block + 1; i <= y; i ++) A[i] += w, A[i] %= Mod;
    }
    for(int i = bel[x] + 1; i < bel[y]; i ++) Add[i] += w, Add[i] %= Mod;
}

inline void Sec_Mul(int x, int y, int w) {
    if(bel[x] == bel[y]) {
        for(int i = (bel[x] - 1) * block + 1; i <= bel[x] * block; i ++) A[i] = (A[i] * Mul[bel[i]] + Add[bel[i]]) % Mod;
        Add[bel[x]] = 0; Mul[bel[x]] = 1;
        for(int i = x; i <= y; i ++) A[i] = (A[i] * w) % Mod; 
        return ;
    } else {
        for(int i = (bel[x] - 1) * block + 1; i <= bel[x] * block; i ++) A[i] = (A[i] * Mul[bel[i]] + Add[bel[i]]) % Mod;
        Add[bel[x]] = 0; Mul[bel[x]] = 1;
        for(int i = x; i <= bel[x] * block; i ++) A[i] = (A[i] * w) % Mod;
        for(int i = (bel[y] - 1) * block + 1; i <= bel[y] * block; i ++) A[i] = (A[i] * Mul[bel[i]] + Add[bel[i]]) % Mod;
        Add[bel[y]] = 0; Mul[bel[y]] = 1;
        for(int i = (bel[y] - 1) * block + 1; i <= y; i ++) A[i] = (A[i] * w) % Mod; 
    }
    for(int i = bel[x] + 1; i < bel[y]; i ++) Add[i] = (Add[i] * w) % Mod, Mul[i] = (Mul[i] * w) % Mod; 
}

int main() {
    n = read();
    for(int i = 1; i <= n; i ++) A[i] = read();
    block = sqrt(n);
    for(int i = 1; i <= n; i ++) bel[i] = (i - 1) / block + 1, Mul[i] = 1;
    int T = n;
    while(T --) {
        int opt = read(), l = read(), r = read(), c = read();
        if(opt == 0) Sec_Add(l, r, c);
        else if(opt == 1) Sec_Mul(l, r, c);
        else cout << (A[r] * Mul[bel[r]] + Add[bel[r]]) % Mod << "\n";
    }
    return 0;
}

 数列分块入门 8

 数列分块入门 9

[Loj] 数列分块

标签:out   min   ack   随机   while   tar   查找   names   mes   

原文地址:https://www.cnblogs.com/shandongs1/p/8976890.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!