[清华集训2014]奇数国
[清华集训2014]奇数国
luogu
UOJ
题意其实是让单点修改,区间求\(\varphi\)
还保证分解之后只会有前60个质数
那么用60个线段树维护区间和即可
还有巧妙的做法是把60个质数是否存在压在long long中线段树维护
考试谁管那么多...
然后BZOJ硬是T了...
#define ri register int
#define ls x<<1,l,mid
#define rs x<<1|1,mid+1,r
#define ll long long
#include<bits/stdc++.h>
using namespace std;
const int _=1e5+5,mod=19961993;
inline int re(){
ri x=0,w=1;register char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*w;
}
int n=1e5,m;
int prime[66]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281};
int val[66],js[66],s[60][_<<2];
inline void pu(ri x){
for(int i=0;i<60;i++)s[i][x]=s[i][x<<1]+s[i][x<<1|1];
}
void bu(ri x,ri l,ri r){
if(l==r){s[1][x]=1;return;}
ri mid=(l+r)>>1;bu(ls);bu(rs);
s[1][x]=s[1][x<<1]+s[1][x<<1|1];
}
void upd(ri x,ri l,ri r,ri k){
if(l==r){for(int i=0;i<60;i++)s[i][x]=val[i];return;}ri mid=(l+r)>>1;
if(k<=mid)upd(ls,k);else upd(rs,k);pu(x);
}
void qsum(ri x,ri l,ri r,ri ql,ri qr){
if(ql<=l&&r<=qr){for(int i=0;i<60;i++)js[i]+=s[i][x];return;}
ri mid=(l+r)>>1;if(ql<=mid)qsum(ls,ql,qr);
if(qr>mid)qsum(rs,ql,qr);
}
inline void mul(ri&x,ri y){x=1ll*x*y%mod;}
inline int ksm(ri x,ri y){
ri s=1;
while(y){if(y&1)mul(s,x);mul(x,x);y>>=1;}
return s;
}
int main(){
m=re();
bu(1,1,n);
while(m--){
ri a=re(),b=re(),c=re();
if(a){
for(ri i=0;i<60;i++){
val[i]=0;
while(c%prime[i]==0){
val[i]++;c/=prime[i];
}
}
upd(1,1,n,b);
}
else{
ri ans=1;
memset(js,0,sizeof(js));
qsum(1,1,n,b,c);
for(ri i=0;i<60;i++){
if(!js[i])continue;
if(js[i]>1)mul(ans,ksm(prime[i],js[i]-1));
mul(ans,prime[i]-1);
}
printf("%d\n",ans);
}
}
return 0;
}