【SDOI 2019】快速查询

传送门


problem

Day1 T1

给定一个长度为 n n n 的整数数列 { a n } \{a_n\} {an},有 q q q 次操作,操作有 6 6 6 种,分别为:单点赋值,全局加,全局乘,全局赋值,单点查值,全局求和。

操作的读入有点鬼畜啊,建议仔细读一下。

数据范围: 1 ≤ n ≤ 1 0 9 1≤n≤10^9 1n109 1 ≤ q ≤ 1 0 7 1\le q\le 10^7 1q107


solution

我们先分析一下数据范围。操作有 1 0 7 10^7 107 个,应该是 O ( 1 ) O(1) O(1) 完成每个操作; n n n 又有 1 0 9 10^9 109 那么大,可以排除线段树等数据结构。

然后发现题目的特性:好像只有单点操作全局操作,并没有区间的操作。

我们可以按照线段树的思路,记一 a d d , m u l , s u m add,mul,sum add,mul,sum,表示全局加,全局乘,全局求和。

那么 2 , 3 , 4 , 6 2,3,4,6 2,3,4,6 操作就是套路,不多说了。想一想怎么做单点操作。

对于 1 1 1,也可以开个 unordered_map 直接做,要记录一下最后一个更改此位置的时间。

那么对于 5 5 5,我们可以取 a x a_x ax(就是最后一次单点赋值和最后一次区间赋值中时间靠后的那次),答案就是 a x × m u l + a d d a_x\times mul+add ax×mul+add。那这也提示我们,单点赋值是时不能直接赋 v a l val val,应该是 v a l − a d d m u l \frac{val-add}{mul} mulvaladd

因此我们还要处理处 m u l mul mul 的逆元。


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<tr1/unordered_map>
#define N 100005
#define P 10000019
using namespace std;
using namespace tr1;
int n,q,t,add,mul=1,sum,Last,ans;
int a[105],b[105],inv[P];
unordered_map<int,int>val,Time;
struct query{int op,x,y;}Q[N];
int Add(int x,int y)  {return x+y>P?x+y-P:x+y;}
int Dec(int x,int y)  {return x-y<0?x-y+P:x-y;}
int Mul(int x,int y)  {return 1ll*x*y%P;}
void solve(int i,int T){
	if(Q[i].op==6)  ans=Add(ans,sum);
	else  if(Q[i].op==2)  add=Add(add,Q[i].x),sum=Add(sum,Mul(Q[i].x,n));
	else  if(Q[i].op==3)  mul=Mul(mul,Q[i].x),add=Mul(add,Q[i].x),sum=Mul(sum,Q[i].x);
	else  if(Q[i].op==4)  add=0,mul=1,sum=Mul(Q[i].x,n),val[0]=Q[i].x,Last=T;
	else  if(Q[i].op==5){
		if(Time[Q[i].x]<=Last)  ans=Add(ans,Add(Mul(val[0],mul),add));
		else  ans=Add(ans,Add(Mul(val[Q[i].x],mul),add));
	}
	else{
		if(Time[Q[i].x]<=Last)  sum=Dec(sum,Add(Mul(val[0],mul),add));
		else  sum=Dec(sum,Add(Mul(val[Q[i].x],mul),add));
		sum=Add(sum,Q[i].y),Time[Q[i].x]=T,val[Q[i].x]=Mul(Dec(Q[i].y,add),inv[mul]);
	}
}
int main(){
	scanf("%d%d",&n,&q);
	for(int i=1;i<=q;++i){
		scanf("%d",&Q[i].op);
		if(Q[i].op==6)  continue;
		if(Q[i].op==1)  scanf("%d%d",&Q[i].x,&Q[i].y),Q[i].y=Add(Q[i].y%P,P);
		else  scanf("%d",&Q[i].x),Q[i].x=Add(Q[i].x%P,P);
	}
	scanf("%d",&t);
	for(int i=1;i<=t;++i)
		scanf("%d%d",&a[i],&b[i]);
	inv[1]=1;
	for(int i=2;i<P;++i)  inv[i]=Mul(P-P/i,inv[P%i]);
	for(int i=1;i<=t;++i)
		for(int j=1;j<=q;++j)
			solve((a[i]+1ll*j*b[i])%q+1,(i-1)*q+j);
	printf("%d",ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值