bzoj5529 [SDOI2019]快速查询

传送门
参考博客
很妙的模拟题。一开始以为是数据结构,然而操作只有全局和单点的。
由于 n n n 1 e 9 1e9 1e9,我们可以把单点操作的点记下来并把它们的位置离散化。因为 Q &lt; = 1 e 5 Q&lt;=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);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值