P10680 题解

题目链接

因为是选取 \(2^k\) 张牌进行决斗,所以考虑 ST 表。

我们设 \(f_{i,k}\) 表示的是从第 \(i\) 张牌开始,连续 \(2^k\) 张牌进行决斗,最终剩下的那张牌的力量大小。显然转移就是 \(f_{i,k} = |f_{i,k-1}-f_{i+2^{k-1},k-1}|\)

但是发现修改时完全重构 st 表不太现实,所以考虑根号分治。设一个 \(p = 10\),假设要修改的位置是 \(x\),我们每次只修改 \(k \le p\)\(i \le x \le i+2^k-1\)\(f_{i,k}\),剩下的先不管。

查询的时候,如果 \(k \le p\),答案就是 \(f_{i,k}\)。否则,我们直接暴力搜索直到剩下的人小于等于 \(2^p\) 时,返回 \(f_{i,k}\)

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll mod = 1e9 + 7;
const int N = 200005;
const int INF = 0x3f3f3f3f;
int a[N];
int f[N][20];
int p;
int cal(int x, int y) {
    if (y <= p) return f[x][y];
    return abs(cal(x, y - 1) - cal(x + (1 << (y - 1)), y - 1));
}
int main() {
    int n, q;
    scanf("%d%d", &n, &q);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    for (int i = 1; i <= n; i++) f[i][0] = a[i];
    for (int len = 1; 1 << len <= n; len++) {
        for (int i = 1; i + (1 << len) - 1 <= n; i++) {
            f[i][len] = abs(f[i][len - 1] - f[i + (1 << (len - 1))][len - 1]);
        }
    }
    p = 10;
    while (q--) {
        int op, l, r;
        scanf("%d%d%d", &op, &l, &r);
        if (op == 1) {
            f[l][0] = r;
            a[l] = r;
            for (int len = 1; len <= p; len++) {
                for (int i = max(l - (1 << len) + 1, 1); i <= l && i + (1 << len) - 1 <= n; i++) {
                    f[i][len] = abs(f[i][len - 1] - f[i + (1 << (len - 1))][len - 1]);
                }
            }
        } else {
            if (r <= p) {
                printf("%d\n", f[l][r]);
            } else {
                printf("%d\n", cal(l, r));
            }
        }
    }
    return 0;
}

posted @ 2025-05-07 10:31  jxy2012  阅读(32)  评论(0)    收藏  举报
//雪花飘落效果