1 条题解

  • 0
    @ 2026-10-26 1:01:10

    思路与算法

    树状数组维护数列和,把区间开方落实为若干次单点差值更新。0和1开方后不变,用后继并查集记录从某位置起第一个仍可能变化的下标。操作2时只遍历区间内这些活跃下标,计算向下取整平方根并更新差值;新值≤1就把该位置移出活跃集合。操作1照常用前缀之差求和。

    正确性说明

    每次只跳过已经为0或1的数,它们在今后所有开方操作中都不会变化,所以跳过不影响结果。对其余被访问的位置,更新到精确的整数平方根,并将新旧值之差加入树状数组,从而始终保持树状数组中的和等于真实数列之和。后继结构确保每个尚需改变的位置都被访问,因此所有更新和查询正确。

    复杂度

    在本题0到10^9范围内,一个数至多成功开方5次便不大于1,所以所有成功修改共 O(N) 次。把这一固定次数视为常数,总时间 O((N+M) log N),并有近常数的后继查找开销;空间 O(N)。

    易错点

    本题1查询、2开方,且初始值允许0,不要套用0开方的另一版本。平方根要向下取整,程序会做整数校正。后继查找用迭代路径压缩,避免大量稳定位置组成深链时递归栈溢出。

    参考代码(C++11)

    #include <bits/stdc++.h>
    using namespace std;
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int n;
        cin >> n;
        vector<long long> a(n + 1), bit(n + 1);
        vector<int> p(n + 2);
        iota(p.begin(), p.end(), 0);
        auto find = [&](int u) {
            while (p[u] != u) {
                p[u] = p[p[u]];
                u = p[u];
            }
            return u;
        };
        auto add = [&](int u, long long delta) {
            for (; u <= n; u += u & -u)
                bit[u] += delta;
        };
        auto prefix = [&](int u) {
            long long ans = 0;
            for (; u; u -= u & -u)
                ans += bit[u];
            return ans;
        };
        for (int i = 1; i <= n; i++) {
            cin >> a[i];
            add(i, a[i]);
        }
        for (int i = n; i >= 1; i--)
            if (a[i] <= 1)
                p[i] = find(i + 1);
        int m;
        cin >> m;
        while (m--) {
            int op, l, r;
            cin >> op >> l >> r;
            if (op == 1)
                cout << prefix(r) - prefix(l - 1) << '\n';
            else {
                int i = find(l);
                while (i <= r) {
                    long long v = sqrt((long double)a[i]);
                    while ((v + 1) * (v + 1) <= a[i])
                        v++;
                    while (v * v > a[i])
                        v--;
                    add(i, v - a[i]);
                    a[i] = v;
                    if (v <= 1)
                        p[i] = find(i + 1);
                    i = find(i + 1);
                }
            }
        }
    }
    
    • 1

    信息

    ID
    1630
    时间
    1000ms
    内存
    512MiB
    难度
    普及+
    标签
    递交数
    1
    已通过
    1
    上传者