1 条题解
-
0
自动搬运
来自洛谷,原作者为

liuzhangfeiabc
**搬运于
2025-08-24 22:09:56,当前版本为作者最后更新于2019-05-08 17:58:46,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
题目大意:给定初始全0的序列,支持:单点修改,全局加,全局乘,全局赋值,单点查询,全局查询。要求每次操作O(1)。
现场平衡树过了的神仙深受我一拜。
注意到操作只有单点和全局,而且虽然序列很长但是修改的点最多1e5个,我们就可以把所有的点拎出来重标号。
我们发现,其实最麻烦的地方是怎么查询单点的值,其余直接模拟就好啦。
需要维护的东西有:一个“标准元素”(未被单点修改的元素)的值,全局所有元素的和。
还需要记录一下一个点上一次被单点修改是什么时候以及上一次全局赋值是什么时候。
单点查询时,如果上一次全局赋值在它上一次单点修改之后,那么它的值当然是“标准元素”啦。
否则呢?考虑从上一次单点修改之后,这个元素经历了什么:
设x为上一次单点修改时的初值:
((x×a1+b1)×a2+b2)×a3+b3......
这其中x的贡献为:
x×a1×a2×a3×……
如果在上一次单点修改时,标准元素的值是y,则两者最终相差:
(x-y)×a1×a2×a3×……
因此我们只需要记录下修改时(x-y)的值,再想办法求出这段区间内所有3操作的连乘即可求出答案。
区间连乘怎么求?可以看作是一个前缀乘一个前缀的逆元。
对每个前缀求逆元,只需要像预处理阶乘的逆元那样,求出全局乘积的逆元再倒着算回去。
然而这就做完了吗?你会发现你获得了0分的好成绩。
原因是你可能会除以0。
因此你还需要记录区间内有多少个0,并在求前缀积时把所有为0的项忽略掉。查询时先判断区间内有没有0,没有的话再算前缀乘前缀的逆元就是对的了。
#include<bits/stdc++.h> using namespace std; #define gc getchar() #define pc putchar #define li long long inline li read(){ li x = 0,y = 0,c = gc; while(!isdigit(c)) y = c,c = gc; while(isdigit(c)) x = (x << 1) + (x << 3) + (c ^ '0'),c = gc; return y == '-' ? -x : x; } inline void print(li q){ if(q < 0) pc('-'),q = -q; if(q >= 10) print(q / 10); pc(q % 10 + '0'); } const int mo = 10000019; inline li ksm(li q,li w){ li as = 1; while(w){ if(w & 1) as = as * q % mo; q = q * q % mo; w >>= 1; } return as; } int n,m,t; struct qy{ int op,x,y; }a[100010]; unordered_map<int,int> mp; li qa[110],qb[110]; int bh[10000010],nw,tot; int jc[10000010],nj[10000010],ling[10000010]; int lst[100010]; li vl[100010]; li an; int main(){ int i,j,u,v; n = read();m = read(); for(i = 1;i <= m;++i){ a[i].op = read(); if(a[i].op == 1){ a[i].x = read(); if(!mp[a[i].x]) mp[a[i].x] = ++nw; a[i].x = mp[a[i].x]; a[i].y = (read() % mo + mo) % mo; } else if(a[i].op <= 4){ a[i].x = (read() % mo + mo) % mo; } else if(a[i].op == 5){ a[i].x = read(); if(!mp[a[i].x]) mp[a[i].x] = ++nw; a[i].x = mp[a[i].x]; } } t = read();tot = m * t; for(i = 1;i <= t;++i){ qa[i] = read();qb[i] = read(); } for(i = 1;i <= t;++i) for(j = 1;j <= m;++j) bh[(i - 1) * m + j] = (qa[i] + j * qb[i]) % m + 1; jc[0] = 1; for(i = 1;i <= tot;++i){ if(a[bh[i]].op != 3) jc[i] = jc[i - 1],ling[i] = ling[i - 1]; else if(a[bh[i]].x) jc[i] = 1ll * jc[i - 1] * a[bh[i]].x % mo,ling[i] = ling[i - 1]; else jc[i] = jc[i - 1],ling[i] = ling[i - 1] + 1; } nj[tot] = ksm(jc[tot],mo - 2); for(i = tot - 1;i >= 0;--i){ if(a[bh[i + 1]].op == 3 && a[bh[i + 1]].x) nj[i] = 1ll * nj[i + 1] * a[bh[i + 1]].x % mo; else nj[i] = nj[i + 1]; } li nw1 = 0,nw2 = 0,val,tmp; int lt = 0; for(i = 1;i <= tot;++i){ if(a[bh[i]].op == 1){ u = a[bh[i]].x; if(lt >= lst[u]) val = nw1; else{ v = lst[u]; if(ling[i] - ling[v]) tmp = 0; else tmp = 1ll * jc[i] * nj[v] % mo; val = (nw1 + vl[u] * tmp) % mo; } (nw2 += a[bh[i]].y - val + mo) %= mo; vl[u] = (a[bh[i]].y - nw1 + mo) % mo; lst[u] = i; } else if(a[bh[i]].op == 2){ (nw1 += a[bh[i]].x) %= mo; (nw2 += 1ll * n * a[bh[i]].x) %= mo; } else if(a[bh[i]].op == 3){ (nw1 *= a[bh[i]].x) %= mo; (nw2 *= a[bh[i]].x) %= mo; } else if(a[bh[i]].op == 4){ nw1 = a[bh[i]].x; nw2 = 1ll * n * a[bh[i]].x % mo; lt = i; } else if(a[bh[i]].op == 5){ u = a[bh[i]].x; if(lt >= lst[u]) val = nw1; else{ v = lst[u]; if(ling[i] - ling[v]) tmp = 0; else tmp = 1ll * jc[i] * nj[v] % mo; val = (nw1 + vl[u] * tmp) % mo; } (an += val) %= mo; } else{ (an += nw2) %= mo; } } print(an); return 0; }
- 1
信息
- ID
- 4341
- 时间
- 2000ms
- 内存
- 512MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者