LOJ3110 SDOI[2019]快速查找【模拟】

传送门


SOL

输入有点复杂,仔细读完后发现不同的操作最多只有 1 e 5 1e5 1e5种,离散化线段树算出来都 2 e 8 2e8 2e8,更不要说大常数, T L E TLE TLE是无疑的。(好像有人用平衡树 A A A了?)
分析发现除了单点赋值,查值,其余都是全体的操作,可以 O ( 1 ) O(1) O(1)做。
对于赋值,有
( ( x + b 1 ) ∗ a 1 + b 2 ) ∗ a 2 . . . = x ∗ a 1 ∗ a 2 ∗ . . . + K , K 与 x 无 关 ((x+b_1)*a_1+b_2)*a_2...=x*a_1*a_2*...+K,K与x无关 ((x+b1)a1+b2)a2...=xa1a2...+K,Kx
维护一个 s t d std std,标准值, n o w = s t d − ( l a s s t d − l a s n o w ) ∗ a 1 ∗ a 2 ∗ . . . now=std-(lasstd-lasnow)*a_1*a_2*... now=std(lasstdlasnow)a1a2...

维护前缀乘积和前缀乘积的逆元就行了(离线维护 1 e 5 1e5 1e5个数值的逆元)

注意乘0和全体赋值相当于清空,要特殊讨论。


CODE

#include<bits/stdc++.h>
using namespace std;
#define sf scanf
#define ll long long
#define cs const
#define ri register int
#define gc getchar()
#define in red()
inline int red(){
	int num=0,f=1;char c=gc;
	for(;!isdigit(c);c=gc)if(c=='-')f=-1;
	for(;isdigit(c);c=gc)num=num*10+(c^48);
	return num*f;
}
cs int mod=1e7+19,N=1e5+10,M=1e7+10;
inline int mul(cs int &a,cs int &b){return 1ll*a*b%mod;}
inline void check(int &a){a%=mod;a= a<0 ? a+mod : a;}
inline int add(cs int &a,cs int &b){return a+b>mod? a-mod+b : a+b;}
inline int dec(cs int &a,cs int &b){return a-b<0 ? a+mod-b :a-b;}
inline int ksm(int a,int b){a%=mod;int ans=1;for(;b;b>>=1,a=mul(a,a))if(b&1)ans=mul(ans,a);return ans;}
map<int,int> f,pos;
typedef pair<int,int>pi;
#define fi first
#define se second
struct node{
	int op,id,vl;
}q[N];
int fc[M],ifc[M],rf[N],rnum[N],seq[M];
int n,las0,lasvl,ans,cnt1,cnt2,sum,st,tot,t,Q,up;
pi las[N],ab[N];
inline void update(int i){
	if(las[i].se<las0||las[i].se<lasvl)las[i].fi=0;
}
inline int query(int i,int tim){
	return add(st,mul(las[i].fi,fc[tim]));
}
signed main(){
//	freopen("data.in","r",stdin);
//	freopen("rhj.out","w",stdout);
	n=in;
	Q=in;
	for(ri i=1;i<=Q;++i){
		q[i].op=in;
		if(q[i].op==1){
			q[i].id=in,q[i].vl=in;check(q[i].vl);
			if(!pos[q[i].id])pos[q[i].id]=++cnt1;
			q[i].id=pos[q[i].id];
			if(!f[q[i].vl])f[q[i].vl]=++cnt2,rf[cnt2]=q[i].vl,rnum[cnt2]=ksm(q[i].vl,mod-2);
			q[i].vl=f[q[i].vl];
		}
		if(q[i].op==2||q[i].op==3||q[i].op==4){
			q[i].vl=in;check(q[i].vl);
			if(!f[q[i].vl])f[q[i].vl]=++cnt2,rf[cnt2]=q[i].vl,rnum[cnt2]=ksm(q[i].vl,mod-2);
			q[i].vl=f[q[i].vl];
		}
		if(q[i].op==5){
			q[i].id=in;
			if(!pos[q[i].id])pos[q[i].id]=++cnt1;
			q[i].id=pos[q[i].id];
		}
	}	
	t=in;
	for(ri i=1;i<=t;++i)ab[i].fi=in,ab[i].se=in,ab[i].fi%=Q,ab[i].se%=Q;
	up=t*Q;
	for(ri i=1;i<=up;++i){
		int jj=i/Q,j=i-jj*Q;++jj;if(!j)j=Q,--jj;
		seq[i]=(1ll*ab[jj].fi%Q+1ll*ab[jj].se*j%Q)%Q+1;
	}
	fc[0]=ifc[0]=1;n%=mod;
	for(ri tim=1;tim<=up;++tim){
		int i=seq[tim];
		ifc[tim]=ifc[tim-1];fc[tim]=fc[tim-1];
		if(q[i].op==1){	
			update(q[i].id);
			int now=query(q[i].id,tim);
			sum=dec(sum,now);
			sum=add(sum,rf[q[i].vl]);
			las[q[i].id].se=tim;
			las[q[i].id].fi=mul(dec(rf[q[i].vl],st),ifc[tim]);
		}
		if(q[i].op==2){
			st=add(st,rf[q[i].vl]);
			sum=add(sum,mul(n,rf[q[i].vl]));
		}
		if(q[i].op==3){
			if(rf[q[i].vl]==0){
				las0=tim;lasvl=0;sum=0;
				st=0;
				fc[tim]=1;ifc[tim]=1;
			}
			else{
				st=mul(st,rf[q[i].vl]);
				fc[tim]=mul(fc[tim],rf[q[i].vl]);
				ifc[tim]=mul(ifc[tim],rnum[q[i].vl]);
				sum=mul(sum,rf[q[i].vl]);
			}
		}
		if(q[i].op==4){
			las0=0;lasvl=tim;sum=mul(n,rf[q[i].vl]);
			fc[tim]=1;ifc[tim]=1;
			st=rf[q[i].vl];
		}
		if(q[i].op==5){
			update(q[i].id);
			ans=add(ans,query(q[i].id,tim));
		
		}
		if(q[i].op==6){
			ans=add(sum,ans);
		}
	}
	cout<<ans<<'\n';
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值