- 【清华集训2014】奇数国
- 欧拉函数+线段树,傻逼题……。
- 首先那个\(ax+by=1\)就是在搞笑,想一想有整数的条件就是\(gcd(a,b)=1\),实际上是让你求\(\phi\)。
- 所以现在需要支持两种操作,区间积求\(phi\),单点修改。
- 因为每个数都只有最多\(60\)个不同质因子,所以可以把它分解质因数。
- 然后就能用\(60\)个线段树来维护每个质因子个数。
- 询问是求区间乘积的欧拉函数,然后用公式\(\phi_n=n*\prod(1-\frac {1}{p_i})\)就可以了。
- 复杂度\(O(60*qlogn)\)。
- 线段树可以换成树状数组,常数小很多。
#include<bits/stdc++.h>
#define R register int
#define db double
#define ll long long
using namespace std;
const int N=100001;
const int mod=19961993;
int n,m,op,u,v,ans;
int gi(){
R x=0,k=1;char c=getchar();
while(c!='-'&&(c<'0'||c>'9'))c=getchar();
if(c=='-')k=-1,c=getchar();
while(c<='9'&&c>='0')x=(x<<3)+(x<<1)+c-'0',c=getchar();
return x*k;
}
namespace cpp1{
int tot,mk[N],prm[N];
struct mon{
int s[61];
void init(){memset(s,0,sizeof(s));}
}te[N*4],nw;
int Qpow(R x,R y){
R ans=1,bas=x;
while(y){
if(y&1)ans=1ll*ans*bas%mod;
bas=1ll*bas*bas%mod,y>>=1;
}return ans;
}
mon mul(mon x,mon y){
mon z;
for(R i=1;i<=60;++i)z.s[i]=x.s[i]+y.s[i];
return z;
}
mon rev(R x){
mon z;z.init();
for(R j=1;j<=tot;++j){
while(x%prm[j]==0)
z.s[j]++,x/=prm[j];
}
return z;
}
void init(){
mk[0]=mk[1]=1;
for(R i=2;i<N;++i){
if(!mk[i])prm[++tot]=i;
for(R j=1;j<=tot&&i*prm[j]<N;++j){
mk[i*prm[j]]=1;
if(i%prm[j]==0)break;
}
}tot=60;
}
void upd(R Le,R Ri,R ps,R i){
if(Le==Ri){te[i]=nw;return ;}
R mid=(Le+Ri)>>1,ls=(i<<1),rs=(ls|1);
if(ps<=mid)upd(Le,mid,ps,ls);else upd(mid+1,Ri,ps,rs);
te[i]=mul(te[ls],te[rs]);
}
mon query(R Le,R Ri,R le,R ri,R i){
if(Le==le&&Ri==ri)return te[i];
R mid=(Le+Ri)>>1,ls=(i<<1),rs=(ls|1);
if(ri<=mid)return query(Le,mid,le,ri,ls);
else if(le>mid)return query(mid+1,Ri,le,ri,rs);
else return mul(query(Le,mid,le,mid,ls),query(mid+1,Ri,mid+1,ri,rs));
}
void Main(){
n=100000,m=gi(),init(),nw.init(),nw.s[2]=1;
for(R i=1;i<=n;++i)upd(1,n,i,1);
while(m--){
op=gi(),u=gi(),v=gi();
if(op==1)nw=rev(v),upd(1,n,u,1);
else {
nw=query(1,n,u,v,1),ans=1;
for(R j=1;j<=60;++j)
if(nw.s[j]){
ans=1ll*ans*(prm[j]-1)%mod;
ans=1ll*ans*Qpow(prm[j],nw.s[j]-1)%mod;
}
printf("%d\n",ans);
}
}
}
}
int main(){
cpp1::Main();
return 0;
}