题目大意:
给出一个数字串,q 次以下两种操作:
- 输入 1 x y,修改第 x 个字符为 y
- 输出 2 x y ,代表查询区间 [x,y] 子串所能表示的某进制的最小值,对 1e9+7 取模。
思路:
要得到最小值,显然进制的选择是区间最大值+1。
看操作是单点修改和区间查询,我们考虑用线段树来维护区间上 2-10 进制的值以及区间最大值。
考虑怎样进行区间合并:
假设当前是十进制
14352
/ \
143 53
\[14352 = 143 * 10^2 + 53
\]
可以发现区间 k 进制的合并是由 k 进制下 左孩子节点 * k 进制的右孩子节点的数字个数次幂 + 右孩子节点 所得到。我们需要维护 k 进制下每个节点的值,以及左兄弟节点与之合并时需要的幂次。
进制不合法的情况我们一样可以进行更新操作,因为我们并不会去选用这样的进制,因此其对于我们的结果没有影响。
在实现上,我们可以通过重载结构体中的加号来进行区间的合并,这样线段树主体的结构显得比较清晰明了,并且容易去修改和调试。
Code:
struct Node {
int mx;
vector<ll> val, pre;
Node(int x = 0) : mx(x), val(10 + 1, x), pre(10 + 1) {
for (int i = 1; i <= 10; i++) {
pre[i] = i;
}
}
Node operator+(const Node &node) const {
Node res;
res.mx = max(mx, node.mx);
for (int i = 1; i <= 10; i++) {
res.pre[i] = pre[i] * node.pre[i] % mod;
res.val[i] = (val[i] * node.pre[i] + node.val[i]) % mod;
}
return res;
}
ll operator()() const {
return val[mx + 1];
}
};
#define ls (v << 1)
#define rs (ls | 1)
#define tm ((tl + tr) >> 1)
struct Segment {
int n;
vector<Node> nodes;
Segment(string &s) : n((int)s.size()), nodes(n << 2) {
function<void(int, int, int)> dfs = [&](int v, int tl, int tr) { // 节点标号,维护的区间
if (tl == tr) {
nodes[v] = Node(s[tm - 1] - '0');
} else {
dfs(ls, tl, tm);
dfs(rs, tm + 1, tr);
nodes[v] = nodes[ls] + nodes[rs];
}
};
dfs(1, 1, n);
}
void upd(int x, int y) {
function<void(int, int, int)> dfs = [&](int v, int tl, int tr) {
if (tl == tr) {
nodes[v] = Node(y);
} else {
if (x <= tm)
dfs(ls, tl, tm);
else
dfs(rs, tm + 1, tr);
nodes[v] = nodes[ls] + nodes[rs];
}
};
dfs(1, 1, n);
}
ll query(int x, int y) {
function<Node(int, int, int)> dfs = [&](int v, int tl, int tr) {
if (x <= tl && tr <= y)
return nodes[v];
if (y <= tm)
return dfs(ls, tl, tm);
if (x > tm)
return dfs(rs, tm + 1, tr);
return dfs(ls, tl, tm) + dfs(rs, tm + 1, tr);
};
return dfs(1, 1, n)();
}
};
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int n, q;
cin >> n >> q;
string s;
cin >> s;
Segment seg(s);
while (q--) {
int op, x, y;
cin >> op >> x >> y;
if (op == 1) {
seg.upd(x, y);
} else {
cout << seg.query(x, y) << "\n";
}
}
return 0;
}