传送门
参考博客
很妙的模拟题。一开始以为是数据结构,然而操作只有全局和单点的。
由于
n
n
n有
1
e
9
1e9
1e9,我们可以把单点操作的点记下来并把它们的位置离散化。因为
Q
<
=
1
e
5
Q<=1e5
Q<=1e5。
然后有一个很精妙的维护方式:
我们维护一个
M
u
l
Mul
Mul和
A
d
d
Add
Add标记。对于一个被修改过的点(记其离散化后的下标为
x
x
x),我们用
v
v
v数组维护一个值,使得
(
(
v
[
x
]
∗
M
u
l
)
+
A
d
d
)
((v[x]*Mul)+Add)
((v[x]∗Mul)+Add)为该位置上的真实值。
而对于没有被单点改过的点,答案就是
A
d
d
Add
Add。因为它只会在全局操作中被修改,且初始值为0。相当于全局乘操作记在了
A
d
d
Add
Add标记上(全局加也是),
M
u
l
Mul
Mul标记对这些点无影响,它只用来维护被改过的单点。
c
h
a
n
g
e
:
change:
change:假设值要改成
a
a
a,相当于解方程:
(
(
v
[
x
]
∗
M
u
l
)
+
A
d
d
)
=
a
((v[x]*Mul)+Add)=a
((v[x]∗Mul)+Add)=a,根据
a
,
M
u
l
,
A
d
d
a,Mul,Add
a,Mul,Add可以将
v
[
x
]
v[x]
v[x]解出来。然后把
x
x
x压进栈里。这里我们维护了实时维护了
M
u
l
Mul
Mul的逆元,就是因为这里要用到。注意这里要先更新
s
u
m
sum
sum。
c
o
v
e
r
:
cover:
cover:把栈里的所有点弹出来,v数组要清零!原因如上。
注意乘
0
0
0的情况。这个操作不能把
m
u
l
mul
mul标记变成
0
0
0了。要改为覆盖
0
0
0操作。
其余看代码应该容易理解。
#include<bits/stdc++.h>
using namespace std;
const int mod=1e7+19;
const int maxn=1e5+10;
int n,m,t,ai,bi,now,v[maxn];
int Inv=1,Mul=1,Add=0,sum=0,ans=0;
int s[maxn*100],cnt=0;
struct node{int op,val,pos,inv;}a[maxn];
inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline int mul(int x,int y){return (long long)x*y%mod;}
inline int quickpow(int a,int b,int ret=1){for(;b;b>>=1,a=mul(a,a)) if(b&1) ret=mul(ret,a);return ret;}
inline int inv(int x){return quickpow(x,mod-2);}
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*f;
}
inline void plu(int x){sum=add(sum,mul(n,x)),Add=add(Add,x);}
inline void change(int x,int y){
sum=add(sum,dec(y,add(Add,mul(v[x],Mul))));
v[x]=mul(dec(y,Add),Inv),s[++cnt]=x;
}
inline void cover(int x){
while(cnt) v[s[cnt--]]=0;
Mul=Inv=1,Add=x,sum=mul(n,x);
}
inline void multi(int x,int ix){
if(!x) return cover(0);
sum=mul(sum,x),Mul=mul(Mul,x);
Add=mul(Add,x),Inv=mul(Inv,ix);
}
inline int query(int x){
if(!x) return sum;
return add(Add,mul(Mul,v[x]));
}
int main(){
n=read(),m=read(),n%=mod;
for(int i=1;i<=m;++i){
a[i].op=read();
if(a[i].op==1||a[i].op==5) a[i].pos=read(),s[++cnt]=a[i].pos;
if(a[i].op<=4) a[i].val=(read()%mod+mod)%mod,a[i].inv=quickpow(a[i].val,mod-2);
}sort(s+1,s+cnt+1),cnt=unique(s+1,s+cnt+1)-s-1;
for(int i=1;i<=m;++i) if(a[i].op==1||a[i].op==5)
a[i].pos=lower_bound(s+1,s+cnt+1,a[i].pos)-s;
t=read(),cnt=0;
for(int i=1;i<=t;++i){
ai=read(),bi=read();
for(int j=1;j<=m;++j){
now=(ai+1ll*bi*j)%m+1;
if(a[now].op==1) change(a[now].pos,a[now].val);
if(a[now].op==2) plu(a[now].val);
if(a[now].op==3) multi(a[now].val,a[now].inv);
if(a[now].op==4) cover(a[now].val);
if(a[now].op==5) ans=add(ans,query(a[now].pos));
if(a[now].op==6) ans=add(ans,query(0));
}
}printf("%d\n",ans);
}