一 问题描述
Argestes 有很多爱好,特别喜欢解决查询问题。有一天,Argestes 想出了这样的问题。给出一个由 N 个非负整数组成的序列,a [1],a [2],a[3] ... a [n]。然后对序列进行 M 个操作。操作可以是以下之一。
S X Y:你应该将 a[x] 的值设置为 y (换句话说,执行赋值 a[x] = y)。
Q L R D P:区间[L, R]有多少个数字的第 D 位是 P, L 和 R 之间是序列的索引。
注意:数字的第1位是最低有效位。
二 输入和输出
1 输入
第一行有一个整数 T,表示测试用例的数量。
对于每个测试用例,第一行包含两个数字 N 和 M。第二行包含 N 个整数,由空格分隔:a[1],a[2]...a[n]。数组元素的初始值。接下来的 M 行中的每一行都以字符类型开头。
如果 type==S,则行中将有两个整数:X,Y
如果 type==Q,则行中将有四个整数:L R D P
2 输出
对于每个操作 Q,输出一行包含答案。
三 输入和输出样例
1 输入样例
1
5 7
10 11 12 13 14
Q 1 5 2 1
Q 1 5 1 0
Q 1 5 1 1
Q 1 5 3 0
Q 1 5 3 1
S 1 100
Q 1 5 3 1
2 输出样例
5
1
1
5
0
1
四 分析和设计
1 分析
本问题包括点更新和区间查询。区间查询比较特殊,查询第 D 位是 P 的数有多少个。可以采用分块的方法解决。
2 设计
a 分块
划分块,统计每一块每一位上的数有多少个。block[j][j][k]表示第 i 块中第 j 位上是 k 的数有多少个。
b 查询
查询区间 [l,r] 有多少个数字的第 d 位是 p。
如果该区间属于同一块,则暴力累加块内第 d 位是 p 的数有多少个。
如果该区间包含多块,则累加中间每一块 i 的 block[i][d][p],然后暴力累加左端和右端第 d 位是 p 的数有多少个。
c 更新
将 a[x] 的值更新为y。因为原来 x 所属块己统计了 a[x] 每一位上数的个数,此时需要减去;然后再将新的值 y 累加上即可。
五 代码
package com.platform.modules.alg.alglib.hdu5057;
import static java.lang.Math.sqrt;
public class Hdu5057 {
private int maxn = 100010;
int a[] = new int[maxn];
int belong[] = new int[maxn];
int L[] = new int[maxn];
int R[] = new int[maxn];
// block[i][j][k] 表示第 i 块中第 j 位上是 k 的数有多少个
int block[][][] = new int[400][12][12];
int n;
int m;
int ten[] = {0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};
void build() {
int t = (int) sqrt(n);
int num = n / t;
if (n % t != 0) num++;
for (int i = 1; i <= num; i++) {
// 每块的左右位置,从 1 开始
L[i] = (i - 1) * t + 1;
R[i] = i * t;
}
R[num] = n;
for (int i = 1; i <= n; i++)
belong[i] = (i - 1) / t + 1; // 所属块,从 1 开始
for (int i = 1; i <= n; i++) {
int temp = a[i];
for (int j = 1; j <= 10; j++) { // 位数最多有 10 位 1<=D<=10
block[belong[i]][j][temp % 10]++; // 块,位,位上的数
temp /= 10;
}
}
}
int query(int l, int r, int d, int p) {
int ans = 0;
if (belong[l] == belong[r]) { // 属于同一块
for (int i = l; i <= r; i++) // 暴力统计
if ((a[i] / ten[d]) % 10 == p)
ans++;
return ans;
}
for (int i = belong[l] + 1; i < belong[r]; i++) // 累加中间块
ans += block[i][d][p];
for (int i = l; i <= R[belong[l]]; i++) { // 左端暴力累加
if ((a[i] / ten[d]) % 10 == p)
ans++;
}
for (int i = L[belong[r]]; i <= r; i++) { // 右端暴力累加
if ((a[i] / ten[d]) % 10 == p)
ans++;
}
return ans;
}
void update(int x, int y) {
for (int i = 1; i <= 10; i++) { // 原来的统计数减少
block[belong[x]][i][a[x] % 10]--;
a[x] /= 10;
}
a[x] = y;
for (int i = 1; i <= 10; i++) { // 新的统计数增加
block[belong[x]][i][y % 10]++;
y /= 10;
}
}
public String output = "";
public String cal(String input) {
String[] line = input.split("\n");
String[] num = line[0].split(" ");
n = Integer.parseInt(num[0]);
m = Integer.parseInt(num[1]);
String[] key = line[1].split(" ");
for (int i = 1; i <= n; i++) {
a[i] = Integer.parseInt(key[i - 1]);
}
build(); // 划分块
int j = 0;
while (m-- > 0) {
String[] command = line[j + 2].split(" ");
j++;
if (command[0].charAt(0) == 'S') {
int x, y;
x = Integer.parseInt(command[1]);
y = Integer.parseInt(command[2]);
update(x, y);
} else {
int l, r, d, p;
l = Integer.parseInt(command[1]);
r = Integer.parseInt(command[2]);
d = Integer.parseInt(command[3]);
p = Integer.parseInt(command[4]);
output += query(l, r, d, p) + "\n";
}
}
return output;
}
}