2018.10.25【NOIP练习】ZUA球困难综合征(线段树)(CRT)

传送门


解析:

首先,这天坑的出题人。。。

思路肯定是线段树维护区间运算结果,但是两万多的模数怎么维护?

这奇怪的光速。。。光速不是 299792458 m / s 299792458m/s 299792458m/s吗?怎么会变成 29393 29393 29393

是的模数是这道题解决的关键,因为 29393 = 7 × 13 × 17 × 19 29393=7\times 13\times 17\times 19 29393=7×13×17×19

所以我们只需要线段树维护这四个模数的结果,再用 C R T CRT CRT合并一下就行了。

虽然说更新的常数有点大(40多),但是已经比两万多不知道好到哪里去了。


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

inline int getint(){
	re int num;
	re char c;
	while(!isdigit(c=gc()));num=c^48;
	while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
	return num;
}

inline void outint(int a){
	static char ch[13];
	if(a==0)pc('0');
	while(a)ch[++ch[0]]=a-a/10*10,a/=10;
	while(ch[0])pc(ch[ch[0]--]^48);
}

inline int quickpow(int a,int b,int mod,int ans=1){
	for(;b;b>>=1,a=a*a%mod)if(b&1)ans=ans*a%mod;
	return ans;
}

inline char getop(){
	re char c;
	while((c=gc())!='^'&&c!='*'&&c!='+');
	return c;
}

cs int M=29393;//7*13*17*19
cs int N=100005;
struct node{int remain[4][20];}t[N<<1],Ans;
cs int mod[4]={7,13,17,19};
int remain[4];
int inv[4][20];
inline int CRT(){
	int ans=0;
	for(int re i=0;i<4;++i)
	ans=(ans+(M/mod[i])*inv[i][(M/mod[i])%mod[i]]%M*remain[i]%M)%M;
	return ans;
}

inline int solve(char op,int val,int now,int mod){
	switch(op){
		case '+':return (val+now)%mod;
		case '*':return val*now%mod;
		case '^':return quickpow(now,val,mod);
	}
}

inline void pushup(int k){
	for(int re i=0;i<4;++i){
		for(int re j=0;j<mod[i];++j)
		t[k].remain[i][j]=t[k<<1|1].remain[i][t[k<<1].remain[i][j]]; 
	}
}

inline void build(int k,int l,int r){
	if(l==r){
		char op=getop();
		int val=getint();
		for(int re i=0;i<4;++i){
			for(int re j=0;j<mod[i];++j)
			t[k].remain[i][j]=solve(op,val,j,mod[i]);
		}
		return ; 
	}
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	pushup(k);
}

inline void update(int k,int l,int r,cs int &pos){
	if(l==r){
		char op=getop();
		int val=getint();
		for(int re i=0;i<4;++i){
			for(int re j=0;j<mod[i];++j)
			t[k].remain[i][j]=solve(op,val,j,mod[i]);
		}
		return ;
	}
	int mid=(l+r)>>1;
	if(pos<=mid)update(k<<1,l,mid,pos);
	else update(k<<1|1,mid+1,r,pos);
	pushup(k);
}

inline void solve(int x){
	for(int re i=0;i<4;++i)remain[i]=t[1].remain[i][x%mod[i]];
	outint(CRT()),pc('\n');
}

int n,m; 
signed main(){
	for(int re i=0;i<4;++i)inv[i][0]=inv[i][1]=1;
	for(int re j=0;j<4;++j){
		for(int re i=2;i<mod[j];++i){
			inv[j][i]=(mod[j]-mod[j]/i)*inv[j][mod[j]%i]%mod[j];
		}
	}
	
	n=getint();
	m=getint();
	build(1,1,n);
	while(m--){
		int op=getint(),x=getint();
		switch(op){
			case 1:{solve(x);break;}
			case 2:{update(1,1,n,x);break;}
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值