题解 P5358 【[SDOI2019]快速查询】

龙之吻—水货

2019-05-08 08:39:31

Solution

# [SDOI2019]快速查询 ## 题目大意 在 $O(n)$ 的时间复杂度内,完成题目所给的 $6$ 种操作。 ## 解题报告 场外同步赛选手,感觉此题并不难? QwQ ### 对于子任务一的做法 发现允许 $O(nlogn)$ 的时间复杂度,离散化之后就变成了了[【模板】线段树 2](https://www.luogu.org/problemnew/show/P3373),似乎比那个还简单不少,因为操作都是全局的。 ### 对于 $100%$ 的做法 发现操作数一共有 $10^5 \times 100 = 10^7$ 不做到每次 $O(1)$ 的时间复杂度肯定是不行了。 自习思考发现这道题唯一的难点就是操作 $5$,如果可以在 $O(1)$ 的时间复杂度内完成这个操作的话,在在 $O(1)$ 的时间复杂度内完成其他的操作似乎并不难。 所以我们现在只需要考虑操作 $5$ 即可。 如果想到了 $50%$ 的做法,那么不难发现有一个东西可以用到,就是线段树的懒标记,这个懒标记记录了两个值 $mul$ 和 $add$ 表示如果原来的数为 $x$ 那么它应该变为 $x \times mul + add$。 发现我们可以很轻松地维护出来这个标记在全局的前缀和,那么,这个东西是否可满足可减性呢?换句话说,我们是否能够通过 $sum_r - sum_{l-1}$ 来得到 $x$ 从 $l$ 开始,到 $r$ 这一段操作之后变成了什么呢? 思考一下这个问题,假设我们知道 $mul_r, add_r, mul_{l-1}, add_{l-1}$ 是否能求出 $sum_r - sum_{l-1}$ 呢? 我们发现,乘一个数会对 $mul$ 和 $add$ 产生影响,而加一个数只会对 $add$ 产生影响,所以 $\frac{mul_r}{mul_{l-1}}$ 就是这段操作所乘的值,求出这个值后,这段操作所加的值也可以很轻易地求出来,也就是 $add_r - add_{l-1} \times \frac{mul_r}{mul_{l-1}}$。 这样,我们只需要存储一个前缀和,之后用一个 hash 表或者离散化一下记录每个数最后在哪个操作里面被改变就可以了,由于是在取模的意义下,所以需要一个线性求逆元。然后我们就可以完成操作 $5$ 了,之后,其他的操作就十分简单,也就不细说了。 时间复杂度 $O(n)$, Accepted! 附上考场(同步赛) Code : ```cpp #include<cstdio> #include<algorithm> #include<tr1/unordered_map> class Solution{ private : static const int maxn = 1e5 + 7; static const int maxm = 1e2 + 7; static const int mod = 1e7 + 19; struct Oper{ int op, pos, val; }; struct Val{ int mul, add; }; int n, q, t, a[maxm], b[maxm], inv[mod], id[maxn * maxm], sum, nowVal, start; int tp, res, ans, vl[maxn]; Oper p[maxn]; Val v[maxn * maxm]; std::tr1::unordered_map<int, int> last; inline void make(int &x) { if (x >= mod) { x -= mod; } } inline int query(int pos) { if (!last.count(pos)) { return nowVal; } //bool flag = pos == 5; pos = last[pos]; if (pos < start) { return nowVal; } int mul = 1ll * v[tp].mul * inv[v[pos].mul] % mod; /* if (flag) { //fprintf(stderr, "%d %d\n", v[tp].mul, v[pos].mul); } */ int add = v[tp].add + mod - 1ll * v[pos].add * mul % mod; make(add); return (1ll * p[id[pos]].val * mul + add) % mod; } public : Solution() { get(); solve(); } void get() { scanf("%d", &n); scanf("%d", &q); for (register int i = 1; i <= q; i++) { scanf("%d", &p[i].op); if (p[i].op == 1) { scanf("%d %d", &p[i].pos, &p[i].val); } else if (p[i].op == 5) { scanf("%d", &p[i].pos); } else if (p[i].op == 6) { continue; } else { scanf("%d", &p[i].val); } p[i].val = (p[i].val % mod + mod) % mod; } scanf("%d", &t); for (register int i = 1; i <= t; i++) { scanf("%d %d", a + i, b + i); } } void solve() { inv[1] = 1; for (register int i = 2; i < mod; i++) { inv[i] = 1ll * (mod - (mod / i)) * inv[mod % i] % mod; } v[0].mul = 1; for (register int i = tp = 1, ed = q * t; i <= ed; i++, tp++) { int A = i / q + 1, B = i % q; if (B == 0) { A--, B = q; } id[i] = (a[A] + 1ll * B * b[A]) % q + 1; int op = p[id[i]].op, pos = p[id[i]].pos, val = p[id[i]].val; //fprintf(stderr, "%d %d %d\n", op, pos, val); if (op == 1) { v[i] = v[i - 1]; int lastVal = query(pos); make(sum += mod - lastVal); make(sum += val); last[pos] = i; } else if (op == 2) { v[i] = v[i - 1]; make(v[i].add += val); make(nowVal += val); make(sum += 1ll * n * val % mod); } else if (op == 3) { v[i].mul = 1ll * v[i - 1].mul * val % mod; v[i].add = 1ll * v[i - 1].add * val % mod; nowVal = 1ll * nowVal * val % mod; sum = 1ll * sum * val % mod; } else if (op == 4) { v[i].mul = 1; nowVal = val, sum = 1ll * nowVal * n % mod; start = i; } else if (op == 5) { v[i] = v[i - 1]; res = query(pos); //fprintf(stderr, "%d\n", res); make(ans += res); } else { v[i] = v[i - 1]; res = sum; //fprintf(stderr, "%d\n", res); make(ans += res); } } printf("%d\n", ans); } }; Solution sol; int main() {} ```