题目显然让求

φi=lrai

能够用线段树维护一下乘积然后求逆元再求欧拉函数,用压位的方法能够缩小60倍的常数。

考虑一下树状数组的做法。由于仅仅有60个质因子,所以能够开60个树状数组维护每个质因子,最初维护了前缀的乘积然后T飞了。由于乘法比起加法还是比較慢的所以能够维护一个前缀的指数和,这样就能够在BZOJ成功卡进最后一页QAQ,然而UOJ的Extra Test还是过不了(应该是我写的代码太丑的原因吧。。

#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<set>
#include<map>
#define ll long long
#define mod 19961993
#define N 100001
using namespace std;
int sc()
{
    int i=0,f=1; char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9')i=i*10+c-'0',c=getchar();
    return i*f;
}
int prime[66],inv[66],b[300],top;
int tr[61][N],n,Q,a[N],flag;
ll cal(ll x,ll y)
{
    ll ans=1;
    for(;y;x=x*x%mod,y>>=1)
        if(y&1)ans=ans*x%mod;
    return ans;
}
void change(int x,int y,ll v)
{
    for(;y<N;y+=y&-y)tr[x][y]+=v;
}
ll ask(int  x,int y)
{
    ll ans=0;
    for(;y;y-=y&-y)ans+=tr[x][y];
    return ans;
}
void solve(int p,ll x,int i,int f)
{
    ll now=0;
    while(x%prime[i]==0)x/=prime[i],now++;
    change(i,p,f*now);
}
int main()
{
    for(int i=2;i<=281;i++)
        if(!b[i])
        {
            prime[++top]=i;inv[top]=cal(prime[top],mod-2);
            for(int j=2*i;j<=281;j+=i)b[j]=1;
        }
    for(int i=1;i<N;i++)change(2,i,1),a[i]=3;
    Q=sc();
    while(Q--)
    {
        if(sc())
        {
            int x=sc(),y=sc();
            for(int i=1;i<=60;i++)
            {
                if(a[x]%prime[i]==0) solve(x,a[x],i,-1);
                if(y%prime[i]==0) solve(x,y,i,1);
            }
            a[x]=y;
        }
        else
        {
            int l=sc(),r=sc();
            ll ans=1;
            for(int i=1;i<=60;i++)
            {
                ll res=ask(i,r)-ask(i,l-1);
                if(res)
                    ans=ans*cal(prime[i],res-1)*(prime[i]-1)%mod;
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}