题目显然让求
φ(∏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;
}
时间: 2024-11-06 03:04:19