牛客寒假算法基础集训营4 B 进制(线段树)

进制

题目大意:

给出一个数字串,q 次以下两种操作:

  1. 输入 1 x y,修改第 x 个字符为 y
  2. 输出 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;
}
posted @ 2022-03-01 15:13  Nepenthe8  阅读(32)  评论(0编辑  收藏  举报