1. 程式人生 > 其它 >LOJ6281. 數列分塊入門 5 題解

LOJ6281. 數列分塊入門 5 題解

題目連結:https://loj.ac/p/6281

涉及操作:

  1. 區間開方;
  2. 區間求和。

解題思路:

主要思路:\(2^{32}\) 次方內的數最多開 \(7\) 次方都會變成 \(1\)

\(tag_i\) 表示第 \(i\) 個分塊的整體開方次數,則 \(tag_i \ge 7\) 時這個分塊中的所有元素都為 \(1\)

示例程式:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 50050;
int n, blo, bl[maxn], a[maxn], sum[505], tag[505];

/**
tag[i]記錄第i個分塊的數值和
sum[i]第i個塊所有單獨增加的元素和
*/

void kaifang(int l, int r) {
    for (int i = l; i <= min(bl[l]*blo, r); i ++) {
        sum[bl[i]] -= a[i];
        a[i] = sqrt(a[i]);
        sum[bl[i]] += a[i];
    }
    if (bl[l] != bl[r]) {
        for (int i = (bl[r]-1)*blo+1; i <= r; i ++) {
            sum[bl[i]] -= a[i];
            a[i] = sqrt(a[i]);
            sum[bl[i]] += a[i];
        }
    }
    for (int i = bl[l]+1; i < bl[r]; i ++) {
        tag[i] ++;
        if (tag[i] < 7) {
            for (int j = (i-1)*blo+1; j <= i*blo; j ++) {
                sum[bl[j]] -= a[j];
                a[j] = sqrt(a[j]);
                sum[bl[j]] += a[j];
            }
        }
    }
}

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

int main() {
    ios::sync_with_stdio(0);
    cin >> n;
    blo = sqrt(n);
    for (int i = 1; i <= n; i ++) {
        cin >> a[i];
        bl[i] = (i - 1) / blo + 1;
        sum[bl[i]] += a[i];
    }
    for (int i = 0; i < n; i ++) {
        int op, l, r, c;
        cin >> op >> l >> r >> c;
        if (op == 0) kaifang(l, r);
        else cout << query(l, r) << endl;
    }
    return 0;
}