【HDU-5238】Calculator(中国剩余定理CRT+线段树乱搞)

题目链接:https://vjudge.net/problem/HDU-5238

题目大意

给出 \(n\) 个操作,只含有 \(+\)\(*\)\(^\) 计算。有两种操作:

  1. 询问一个数 \(x\),将这个 \(x\) 把所有给出需要的计算式来计算一遍。

  2. 将其中某一步计算修改。

花絮

Hartley第一眼看过去感觉是个数据结构,但在同一场已经有另一道线段树乱搞题,所以感到十分不解,没有想到任何办法来维护。

思路

正如花絮中所说,单点修改+区间查询=数据结构!

如果 \(n\)\(m\) 很小的话,可以对于每一个节点开 \(29393\) 个值,用于保存计算之后的值,通过线段树的 push_up 操作将两个结点合并。合并过程为,先计算左端点的值为 \(val\_left\),最终结果为 \(val[rt<<1|1][val\_left]\)

但是 \(n\)\(m\) 很大,显然这样子开一个这么大的线段树会裂开。

注意到 \(29393\) 非常奇怪,打表之后发现 \(29393 = 7 * 13 * 17 * 19\)

设最终答案为 \(x\),那么可以转化为求解

\(\left\{\begin{matrix} x \equiv a_{1} (\mod 7)\\ x \equiv a_{2} (\mod 13)\\ x \equiv a_{3} (\mod 17)\\ x \equiv a_{4} (\mod 19)\end{matrix}\right.\)

其中,\(a_{i}\) 可以通过线段树求出。

这样能将原先每个节点开 \(29393\) 个值的线段树压缩为每个结点开 \(4 * 20 = 80\) 个值的线段树。

最后套用中国剩余定理模板即可求解。

AC代码

#include <bits/stdc++.h>

typedef long long ll;
using namespace std;
const int MAXN = 5e4 + 5;
const int muls[4] = {7, 13, 17, 19}; // 4 divers


namespace CRT {
    int a[4], m[4];

    void exgcd(int a, int b, int &x, int &y) {
        if (b == 0) {
            x = 1, y = 0;
            return;
        }
        exgcd(b, a % b, x, y);
        int z = x;
        x = y, y = z - y * (a / b);
    }
    int solve() {
        int mul_sum = 29393;
        int ans = 0;
        for (int i = 0; i < 4; i++) {
            int M = mul_sum / m[i];
            int x = 0, y = 0;
            exgcd(M, m[i], x, y);
            ans += a[i] * M * (x < 0 ? x + m[i] : x);
        }
        return ans % mul_sum;
    }
}
using CRT::solve;


int qpow[4][20][MAXN];

void init() {
    for (int i = 0; i < 4; i++) {
        CRT::m[i] = muls[i];
    }


    for (int i = 0; i < 4; i++) {
        int p = muls[i];
        for (int j = 0; j < p; j++) {
            int now = 1;
            for (int k = 0; k < MAXN; k++) {
                qpow[i][j][k] = now;
                now = now * j % p;
            }
        }
    }
}


int opt[MAXN], a[MAXN];

class SEG {
public:
    struct node {
        int l, r;
        int val[4][20];
    } T[MAXN << 2];

    inline void push_up(int rt) {
        for (int i = 0; i < 4; i++) {
            int p = muls[i];
            for (int j = 0; j < p; j++) {
                T[rt].val[i][j] = T[rt << 1 | 1].val[i][T[rt
                        << 1].val[i][j]]; // T[rt<<1].val[i][j] -> calculate left, T[rt<<1|1].val[i][left] -> answer
            }
        }
    }

    void build(int rt, int l, int r) {
        T[rt].l = l, T[rt].r = r;
        if (l == r) {
            for (int i = 0; i < 4; i++) {
                int p = muls[i];
                for (int j = 0; j < p; j++) {
                    int now = j;
                    if (opt[l] == 0) now = (now + a[l]) % p;
                    else if (opt[l] == 1) now = (now * a[l]) % p;
                    else now = qpow[i][now][a[l]]; // now ^ (a[l]) % mul[i]
                    T[rt].val[i][j] = now;
                }
            }
            return;
        }
        int mid = (l + r) >> 1;
        build(rt << 1, l, mid), build(rt << 1 | 1, mid + 1, r);
        push_up(rt);
    }

    void update(int rt, int pos) {
        if (T[rt].l == T[rt].r) {
            for (int i = 0; i < 4; i++) {
                int p = muls[i];
                for (int j = 0; j < p; j++) {
                    int now = j;
                    if (opt[T[rt].l] == 0) now = (now + a[T[rt].l]) % p;
                    else if (opt[T[rt].l] == 1) now = (now * a[T[rt].l]) % p;
                    else now = qpow[i][now][a[T[rt].l]]; // now ^ (a[l]) % mul[i]
                    T[rt].val[i][j] = now;
                }
            }
            return;
        }
        int mid = (T[rt].l + T[rt].r) >> 1;
        if (pos <= mid) update(rt << 1, pos);
        else update(rt << 1 | 1, pos);
        push_up(rt);
    }
} tree;


int main() {
    init();
    int T;
    scanf("%d", &T);

    int kass = 1;
    while (T--) {
        int n, m;
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; i++) {
            char s[10];
            scanf("%s", s);

            if (s[0] == '+') opt[i] = 0;
            else if (s[0] == '*') opt[i] = 1;
            else opt[i] = 2;

            int s_len = strlen(s);
            a[i] = 0;
            for (int j = 1; j < s_len; j++) a[i] = a[i] * 10 + s[j] - '0';
        }
        tree.build(1, 1, n);
        printf("Case #%d:\n", kass++);
        while (m--) {
            int ops;
            scanf("%d", &ops);
            if (ops == 1) {
                int x;
                scanf("%d", &x);

                for (int i = 0; i < 4; i++) {
                    int p = muls[i];
                    CRT::a[i] = tree.T[1].val[i][x % p];
                }

                printf("%d\n", solve());


            } else {
                int pos;
                scanf("%d", &pos);

                char s[10];
                scanf("%s", s);

                if (s[0] == '+') opt[pos] = 0;
                else if (s[0] == '*') opt[pos] = 1;
                else opt[pos] = 2;

                int s_len = strlen(s);
                a[pos] = 0;
                for (int j = 1; j < s_len; j++) a[pos] = a[pos] * 10 + s[j] - '0';

                tree.update(1, pos);
            }
        }

    }
}
posted @ 2020-11-20 16:42  tudouuuuu  阅读(69)  评论(0编辑  收藏  举报