数列分块入门 (1 ~ 7)

分块

6277. 数列分块入门 1

分块思想

我们把每m个元素分成一块,所以我们总共的块数就是 n / m n / m n/m块,一般情况下我们取 m = n m = \sqrt{n} m=n .对于区间加操作,我们可以先暴力左右两边,然后对于中间的整块的部分的加减,我们累加在块的标记上,然后我们每次查询的时候只要,每个元素的值加上这个块的标记值,就可以得到我们的答案了。

复杂度分析

每次操作我们修改的最多的元素最多就是 O ( n ) O(\sqrt {n}) O(n )级别的个数,时间复杂读也就是 O ( n ) O(\sqrt n) O(n )级别的,查询的时间复杂度是 O ( 1 ) O(1) O(1)的,最多有 n n n个操作,整体上是 O ( n n ) O(n \sqrt{n}) O(nn )的,于是一个暴力而优美的分块算法就出现了,简单的思想, \sqrt { } 级别的优化。

代码

/*
  Author : lifehappy
*/
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define endl '\n'

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

const double pi = acos(-1.0);
const double eps = 1e-7;
const int inf = 0x3f3f3f3f;

inline ll read() {
  ll f = 1, x = 0;
  char c = getchar();
  while(c < '0' || c > '9') {
    if(c == '-')    f = -1;
    c = getchar();
  }
  while(c >= '0' && c <= '9') {
    x = (x << 1) + (x << 3) + (c ^ 48);
    c = getchar();
  }
  return f * x;
}

void print(ll x) {
  if(x < 10) {
    putchar(x + 48);
    return ;
  }
  print(x / 10);
  putchar(x % 10 + 48);
}

const int N = 5e4 + 10;

int value[N], bl[N], tag[N], block, n;

void add(int l, int r, int c) {
  for(int i = l; i <= bl[l] * block && i <= r; i++) {//对前面的部分进行暴力修改。
    value[i] += c;
  }
  if(bl[l] != bl[r]) {//如果这两个块不在同一个分块中才要进行后面的暴力修改,否则将会重复累加。
    for(int i = block * (bl[r] - 1) + 1; i <= r; i++) {
      value[i] += c;
    }
  }
  for(int i = bl[l] + 1; i <= bl[r] - 1; i++) {//对每个块进行区间修改。
    tag[i] += c;
  }
  //严格来说每次修改的复杂度最多将会是3 * sqrt(n)
}

int main() {
  // freopen("in.txt", "r", stdin);
  // freopen("out.txt", "w", stdout);
  // ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  n = read(); block = sqrt(n);//block是分块的大小
  for(int i = 1; i <= n; i++) {
    value[i] = read();
    bl[i] = (i - 1) / block + 1;//每个位置在分块中的位置。
  }
  for(int i = 1; i <= n; i++) {
    int op = read(), l = read(), r = read(), c = read();
    if(!op) {
      add(l, r, c);
    }
    else {
      printf("%d\n", value[r] + tag[bl[r]]);//他当前的值加上分块的累加值。
    }
  }
	return 0;
}

6278. 数列分块入门 2

想法

查询操作:对于每一个块我们开一个数组来维护其有序的状态,所以对于每一个整块我们可以通过二分去得到我们的答案,对于两头的块,我们可以暴力去得到满足条件的数。

修改操作:同样的对于整块的我们还是在一个区间标记加上其修改值,对于两头的操作我们需要额外单独的操作,记得操作完之后要把原来的存区间有序的数组重新排序,得到一个新的区间有序数组。

代码

/*
  Author : lifehappy
*/
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define endl '\n'

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

const double pi = acos(-1.0);
const double eps = 1e-7;
const int inf = 0x3f3f3f3f;

inline ll read() {
    ll f = 1, x = 0;
    char c = getchar();
    while(c < '0' || c > '9') {
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9') {
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }
    return f * x;
}

void print(ll x) {
    if(x < 10) {
        putchar(x + 48);
        return ;
    }
    print(x / 10);
    putchar(x % 10 + 48);
}

const int N = 5e4 + 10;

int value[N], bl[N], tag[N], n, block, m;

vector<int> elem[N];

void reset(int x) {//最好还是写一个函数,一开始我就是没写函数然后wa了一次。
    elem[x].clear();
    for(int i = (x - 1) * block + 1; i <= x * block && i <= n; i++)
        elem[x].pb(value[i]);
    sort(elem[x].begin(), elem[x].end());
}

void add(int l, int r, int x) {
    for(int i = l; i <= r && i <= bl[l] * block; i++)
        value[i] += x;
    reset(bl[l]);
    if(bl[l] != bl[r]) {
        for(int i = (bl[r] - 1) * block + 1; i <= r; i++)
            value[i] += x;
        reset(bl[r]);
    }
    for(int i = bl[l] + 1; i <= bl[r] - 1; i++)
     tag[i] += x;
}

int query(int l, int r, int x) {
    int sum = 0;
    for(int i = l; i <= r && i <= bl[l] * block; i++)
        if(value[i] + tag[bl[i]] < x)
            sum++;
    if(bl[l] != bl[r])
        for(int i = (bl[r] - 1) * block + 1; i <= r; i++)
            if(value[i] + tag[bl[i]] < x)
                sum++;
    for(int i = bl[l] + 1; i <= bl[r] - 1; i++) {
        sum += lower_bound(elem[i].begin(), elem[i].end(), x - tag[i]) - elem[i].begin();
    }
    return sum;
}

int main() {
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    // ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    n = read(); block = sqrt(n);
    for(int i = 1; i <= n; i++) {
        value[i] = read();
        bl[i] = (i + block - 1) / block;
        elem[bl[i]].pb(value[i]);
    }
    m = bl[n];
    for(int i = 1; i <= m; i++) sort(elem[i].begin(), elem[i].end());
    for(int i = 1; i <= n; i++) {
        int op = read(), l = read(), r = read(), c = read();
        if(op & 1) {
            printf("%d\n", query(l, r, c * c));
        }
        else {
            add(l, r, c);
        }
    }
	return 0;
}

6279. 数列分块入门 3

想法

这题应该是跟上一个类似,就是 q u e r y query query操作稍微变化一下。

用上面的代码修改的时候要注意,这题数据变大了,需要改成 1 e 5 + 10 1e5 + 10 1e5+10,不然就跟我一样入坑了。

代码

/*
  Author : lifehappy
*/
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define endl '\n'

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

const double pi = acos(-1.0);
const double eps = 1e-7;
const int inf = 0x3f3f3f3f;

inline ll read() {
    ll f = 1, x = 0;
    char c = getchar();
    while(c < '0' || c > '9') {
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9') {
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }
    return f * x;
}

void print(ll x) {
    if(x < 10) {
        putchar(x + 48);
        return ;
    }
    print(x / 10);
    putchar(x % 10 + 48);
}

const int N = 1e5 + 10;

int value[N], bl[N], tag[N], n, block, m;

vector<int> elem[N];

void reset(int x) {
    elem[x].clear();
    for(int i = (x - 1) * block + 1; i <= x * block && i <= n; i++)
        elem[x].pb(value[i]);
    sort(elem[x].begin(), elem[x].end());
}

void add(int l, int r, int x) {
    for(int i = l; i <= r && i <= bl[l] * block; i++)
        value[i] += x;
    reset(bl[l]);
    if(bl[l] != bl[r]) {
        for(int i = (bl[r] - 1) * block + 1; i <= r; i++)
            value[i] += x;
        reset(bl[r]);
    }
    for(int i = bl[l] + 1; i <= bl[r] - 1; i++)
     tag[i] += x;
}

ll query(int l, int r, int x) {
    ll ans = -1e10;
    for(int i = l; i <= r && i <= bl[l] * block; i++)
        if(value[i] + tag[bl[i]] < x && value[i] + tag[bl[i]] > ans)
            ans = value[i] + tag[bl[i]];
    if(bl[l] != bl[r])
        for(int i = (bl[r] - 1) * block + 1; i <= r; i++)
            if(value[i] + tag[bl[i]] < x && value[i] + tag[bl[i]] > ans)
                ans = value[i] + tag[bl[i]];
    for(int i = bl[l] + 1; i <= bl[r] - 1; i++) {
        auto p = lower_bound(elem[i].begin(), elem[i].end(), x - tag[i]);
        if(p == elem[i].begin()) continue;
        p--;
        ans = max(ans, 1ll * (*p + tag[i]));
    }
    return ans == -1e10 ? -1 : ans;
}

int main() {
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    // ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    n = read(); block = sqrt(n);
    for(int i = 1; i <= n; i++) {
        value[i] = read();
        bl[i] = (i + block - 1) / block;
        elem[bl[i]].pb(value[i]);
    }
    m = bl[n];
    for(int i = 1; i <= m; i++) sort(elem[i].begin(), elem[i].end());
    for(int i = 1; i <= n; i++) {
        int op = read(), l = read(), r = read(), c = read();
        if(op & 1) {
            printf("%d\n", query(l, r, c));
        }
        else {
            add(l, r, c);
        }
    }
	return 0;
}

6280. 数列分块入门 4

想法

仿照题二,我们可以再引入一个数组来代表当前块的总和,之后的操作就变得简单了。

代码

/*
  Author : lifehappy
*/
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define endl '\n'

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

const double pi = acos(-1.0);
const double eps = 1e-7;
const int inf = 0x3f3f3f3f;

inline ll read() {
    ll f = 1, x = 0;
    char c = getchar();
    while(c < '0' || c > '9') {
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9') {
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }
    return f * x;
}

void print(ll x) {
    if(x < 10) {
        putchar(x + 48);
        return ;
    }
    print(x / 10);
    putchar(x % 10 + 48);
}

const int N = 1e5 + 10;

ll a[N], sum[N], tag[N];

int n, block, bl[N];

void update(int l, int r, int x) {
    for(int i = l; i <= bl[l] * block && i <= r; i++) {
        a[i] += x;
        sum[bl[i]] += x;
    }
    if(bl[l] != bl[r]) {
        for(int i = (bl[r] - 1) * block + 1; i <= r; i++) {
            a[i] += x;
            sum[bl[i]] += x;
        }
    }
    for(int i = bl[l] + 1; i <= bl[r] - 1; i++)
        tag[i] += x;
}

int query(int l, int r, int mod) {
    ll ans = 0;
    for(int i = l; i <= bl[l] * block && i <= r; i++) {
        ans = (ans + a[i] + tag[bl[i]]) % mod;
    }
    if(bl[l] != bl[r]) {
        for(int i = (bl[r] - 1) * block + 1; i <= r; i++) {
            ans = (ans + a[i] + tag[bl[i]]) % mod;
        }
    }
    for(int i = bl[l] + 1; i <= bl[r] - 1; i++) {
        ans = (ans + sum[i] + tag[i] * block) % mod;
    }
    return ans;
}

int main() {
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    // ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    n = read(); block = sqrt(n);
    for(int i = 1; i <= n; i++) {
        a[i] = read();
        bl[i] = (i + block - 1) / block;
        sum[bl[i]] += a[i];
    }
    for(int i = 1; i <= n; i++) {
        int op = read(), l = read(), r = read(), c = read();
        if(op & 1) {
            printf("%d\n", query(l, r, c + 1));
        }
        else {
            update(l, r, c);
        }
    }
	return 0;
}

6281. 数列分块入门 5

想法

一开始看到这个题目感觉分块无从下手啊,区间开方操作,结果一看他们ac代码,原来是开根号,,,

既然是开根号那就简单了,学过线段树应该写过这类题,我们只要加一个区间tag来标记区间内所有的数是否全部变成0和1即可。然后再加一个区间和,对于tag标记为,区间所有的数都是0 和1的我们可以直接查询区间和,也可以跳过他的开方显然有 1 = 1 \sqrt {1} = 1 1 =1 0 = 0 \sqrt {0} = 0 0 =0

代码

/*
  Author : lifehappy
*/
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define endl '\n'

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

const double pi = acos(-1.0);
const double eps = 1e-7;
const int inf = 0x3f3f3f3f;

inline ll read() {
    ll f = 1, x = 0;
    char c = getchar();
    while(c < '0' || c > '9') {
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9') {
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }
    return f * x;
}

void print(ll x) {
    if(x < 10) {
        putchar(x + 48);
        return ;
    }
    print(x / 10);
    putchar(x % 10 + 48);
}

const int N = 1e5 + 10;

int a[N], sum[N], tag[N];

int n, block, bl[N];

void update(int l, int r) {
    for(int i = l; i <= r && i <= bl[l] * block; i++) {
        if(a[i] == 0 || a[i] == 1) continue;
        sum[bl[i]] -= a[i];
        a[i] = sqrt(a[i]);
        sum[bl[i]] += a[i];
        if(a[i] == 1 || a[i] == 0) tag[bl[i]]++;
    }
    if(bl[l] != bl[r]) {
        for(int i = (bl[r] - 1) * block + 1; i <= r; i++) {
            if(a[i] == 0 || a[i] == 1) continue;
            sum[bl[i]] -= a[i];
            a[i] = sqrt(a[i]);
            sum[bl[i]] += a[i];
            if(a[i] == 1 || a[i] == 0) tag[bl[i]]++;
        }
    }
    for(int i = bl[l] + 1; i <= bl[r] - 1; i++) {
        if(tag[i] == block) continue;
        else {
            for(int j = (i - 1) * block + 1; j <= i * block; j++) {
                if(a[j] == 0 || a[j] == 1) continue;
                sum[bl[j]] -= a[j];
                a[j] = sqrt(a[j]);
                sum[bl[j]] += a[j];
                if(a[i] == 1 || a[i] == 0) tag[bl[i]]++;
            }
        }
    }
}

int query(int l, int r) {
    int ans = 0;
    for(int i = l; i <= r && i <= bl[l] * block; i++)
        ans += a[i];
    if(bl[l] != bl[r]) {
        for(int i = (bl[r] - 1) * block + 1; i <= r; i++)
            ans += a[i];
    }
    for(int i = bl[l] + 1; i <= bl[r] - 1; i++) {
        if(tag[i] == block) ans += sum[i];
        else {
            for(int j = (i - 1) * block + 1; j <= i * block; j++)
                ans += a[j];
        }
    }
    return ans;
}

int main() {
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    // ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    n = read(); block = sqrt(n);
    for(int i = 1; i <= n; i++) {
        a[i] = read();
        bl[i] = (i + block - 1) / block;
        sum[bl[i]] += a[i];
        if(a[i] == 0 || a[i] == 1) tag[bl[i]]++;
    }
    for(int i = 1; i <= n; i++) {
        int op = read(), l = read(), r = read(), c = read();
        if(op & 1) {
            printf("%d\n", query(l, r));
        }
        else {
            update(l, r);
        }
    }
	return 0;
}

6282. 数列分块入门 6

想法

这里引入了一个暴力的 r e b u i l d rebuild rebuild,不愧是分块,依旧是如此的暴力。

代码

/*
  Author : lifehappy
*/
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define endl '\n'

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

const double pi = acos(-1.0);
const double eps = 1e-7;
const int inf = 0x3f3f3f3f;

inline ll read() {
    ll f = 1, x = 0;
    char c = getchar();
    while(c < '0' || c > '9') {
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9') {
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }
    return f * x;
}

void print(ll x) {
    if(x < 10) {
        putchar(x + 48);
        return ;
    }
    print(x / 10);
    putchar(x % 10 + 48);
}

const int N = 2e5 + 10;

int value[N], n, m, block;

vector<int> a[N];

pii query(int x) {
    int now = 1;
    while(x > a[now].size()) {
        x -= a[now].size(), now++;
    }
    return mp(now, x - 1);
}

void rebuild() {
    int num = 0;
    for(int i = 1; i <= m; i++) {
        for(int j = 0; j < a[i].size(); j++) {
            value[++num] = a[i][j];
        }
        a[i].clear();//注意一定要clear,我就在这里找bug找了半小时。
    }
    block = sqrt(num), m = (num + block - 1) / block;
    for(int i = 1; i <= num; i++) {
        a[(i + block - 1) / block].pb(value[i]);
    }
}

void insert(int pos, int value) {
    pii p = query(pos);
    a[p.first].insert(a[p.first].begin() + p.second, value);
    if(a[p.first].size() > 20 * block)
        rebuild();
}

int main() {
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    // ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    n = read();
    block = sqrt(n), m = (n + block - 1) / block;
    for(int i = 1; i <= n; i++) {
        value[i] = read();
        a[(i + block - 1) / block].pb(value[i]);
    }
    for(int i = 1; i <= n; i++) {
        int op = read(), l = read(), r = read(), c = read();
        if(op & 1) {
            pii ans = query(r);
            printf("%d\n", a[ans.first][ans.second]);
        }
        else {
            insert(l, r);
        }
    }
	return 0;
}

6283. 数列分块入门 7

想法

暴力两头,区间操作中间。

代码

/*
  Author : lifehappy
*/
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define endl '\n'

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

const double pi = acos(-1.0);
const double eps = 1e-7;
const int inf = 0x3f3f3f3f;

inline ll read() {
    ll f = 1, x = 0;
    char c = getchar();
    while(c < '0' || c > '9') {
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9') {
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }
    return f * x;
}

void print(ll x) {
    if(x < 10) {
        putchar(x + 48);
        return ;
    }
    print(x / 10);
    putchar(x % 10 + 48);
}

const int N = 1e5 + 10, mod = 1e4 + 7;

int a[N], add[N], mult[N], bl[N], n, block;

void init(int bl) {
    for(int i = (bl - 1) * block + 1; i <= bl * block; i++) {
        a[i] = (a[i] * mult[bl]) % mod;
        a[i] = (a[i] + add[bl]) % mod;
    }
    mult[bl] = 1, add[bl] = 0;
}

void Add(int l, int r, int x) {
    init(bl[l]);
    for(int i = l; i <= r && i <= bl[l] * block; i++)
        a[i] = (a[i] + x) % mod;
    if(bl[l] != bl[r]) {
        init(bl[r]);
        for(int i = (bl[r] - 1) * block + 1; i <= r; i++)
            a[i] = (a[i] + x) % mod;
    }
    for(int i = bl[l] + 1; i <= bl[r] - 1; i++)
        add[i] = (add[i] + x) % mod;
}

void Mult(int l, int r, int x) {
    init(bl[l]);
    for(int i = l; i <= r && i <= bl[l] * block; i++)
        a[i] = (a[i] * x) % mod;
    if(bl[l] != bl[r]) {
        init(bl[r]);
        for(int i = (bl[r] - 1) * block + 1; i <= r; i++)
            a[i] = (a[i] * x) % mod;
    }
    for(int i = bl[l] + 1; i <= bl[r] - 1; i++)
        mult[i] = (mult[i] * x) % mod, add[i] = (add[i] * x) % mod;
}

int query(int pos) {
    return (((a[pos] * mult[bl[pos]])) % mod + add[bl[pos]]) % mod;
}

int main() {
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    // ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    n = read();
    block = sqrt(n);
    for(int i = 1; i <= n; i++) {
        a[i] = read() % mod;
        bl[i] = (i + block - 1) / block;
        mult[i] = 1;
    }
    for(int i = 1; i <= n; i++) {

        int op = read(), l = read(), r = read(), x = read();
        if(!op) {
            Add(l, r, x);
        } else if(op & 1) {
            Mult(l, r, x);
        } else {
            printf("%d\n", query(r));
        }
    }
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值