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;
}

浙公网安备 33010602011771号