【YbtOJ#911】欧拉函数
题目
题目链接:https://www.ybtoj.com.cn/contest/125/problem/1
\(n\leq 50000;Q\leq 10^5;a_i,x\leq 40000\),除了操作编号,其余数据全部随机。时限 \(3.5s\)。
思路
数据随机,考虑乱搞。
首先需要一个线段树维护区间和以及区间乘积 \(\bmod 10^9+7\)。
对于操作一,区间和 \(\leq 2\times 10^9\),考虑 洛谷P5071 [Ynoi2015] 此时此刻的光辉 的套路,因为 \(1300^3>2\times 10^9\),我们直接暴力求出这个数 \(1300\) 内的质因子,然后剩余最多两个超过 \(1300\) 的质因子,用 Pollard-Rho 求出其中一个,另外一个除一下就出来了。
这部分的单次复杂度为 \(O(\log n+1300+\text{PR 玄学复杂度})\)。忽略掉 PR 的复杂度以及常数的话,极限大约需要执行 131,600,000 次。
对于操作二,因为是区间乘积,所以保证了这个数的所有质因子都是不超过 \(40000\) 的。考虑直接暴力求出所有的质因子。
在线段树上每一个节点维护一个 bitset,表示这个区间的数的质因子集合。然后每次直接把 \([l,r]\) 的 bitset 暴力合并。
接下来直接无脑暴力枚举所有不超过 \(40000\) 的质数(\(4202\) 个),然后更新答案即可。
这部分单词时间复杂度为 \(O(\log n\log 40000+4202)\),忽略常数极限大约需要执行 445,800,000 次。
但是由于这道题数据随机,所以实际上常数非常小。可以通过。
正解是复杂度 \(O((n+Q)k\log^2 n)\) 的二维数点,其中 \(k\) 是一个数不同的质因子数量。
代码
怎么看我这个都比正解长。。。
#include <bits/stdc++.h>
#define reg register
using namespace std;
typedef long long ll;
const int N=50010,M=4206,Lim=2000,MOD=1e9+7;
const int prime[3]={2,7,61};
int n,m,Q,maxd,a[N],prm[N],inv[N],id[N];
bool v[N];
bitset<M> g,f[N];
int gcd(int x,int y)
{
return y?gcd(y,x%y):x;
}
int fpow(int x,int k,int mod=MOD)
{
int ans=1;
for (;k;k>>=1,x=1LL*x*x%mod)
if (k&1) ans=1LL*ans*x%mod;
return ans;
}
void findprm(int n)
{
for (reg int i=2;i<=n;i++)
if (!v[i])
{
prm[++m]=i; id[i]=m;
inv[m]=fpow(i,MOD-2)%MOD;
for (reg int j=1;j*i<=n;j++)
v[j*i]=1;
}
}
void getfac(int n)
{
for (reg int i=1;i<=n;i++)
{
int p=i;
for (reg int j=1;prm[j]*prm[j]<=p;j++)
if (!(p%prm[j]))
{
f[i][j]=1;
while (!(p%prm[j])) p/=prm[j];
}
if (p>1) f[i][id[p]]=1;
}
}
struct SegTree
{
int sum[N*4],mul[N*4];
bitset<M> d[N*4];
void pushup(int x)
{
d[x]=d[x*2]|d[x*2+1];
sum[x]=sum[x*2]+sum[x*2+1];
mul[x]=1LL*mul[x*2]*mul[x*2+1]%MOD;
}
void update(int x,int l,int r,int k,int v)
{
if (l==r)
{
d[x].reset(); d[x]|=f[v];
sum[x]=mul[x]=v;
return;
}
int mid=(l+r)>>1;
if (k<=mid) update(x*2,l,mid,k,v);
else update(x*2+1,mid+1,r,k,v);
pushup(x);
}
int query1(int x,int l,int r,int ql,int qr)
{
if (ql<=l && qr>=r) return sum[x];
int mid=(l+r)>>1,res=0;
if (ql<=mid) res+=query1(x*2,l,mid,ql,qr);
if (qr>mid) res+=query1(x*2+1,mid+1,r,ql,qr);
return res;
}
int query2(int x,int l,int r,int ql,int qr)
{
if (ql<=l && qr>=r)
{
g|=d[x];
return mul[x];
}
int mid=(l+r)>>1,res=1;
if (ql<=mid) res=1LL*res*query2(x*2,l,mid,ql,qr)%MOD;
if (qr>mid) res=1LL*res*query2(x*2+1,mid+1,r,ql,qr)%MOD;
return res;
}
}seg;
bool MR(int n)
{
for (reg int i=0;i<=2;i++)
{
if (n==prime[i]) return 1;
int p=n-1,pw=fpow(prime[i],p,n);
while (pw==1 && !(p&1))
p>>=1,pw=fpow(prime[i],p,n);
if (pw!=1 && pw!=n-1) return 0;
}
return 1;
}
int work(int n)
{
int s=0,t=0,c=rand()%(n-1)+1,val=1,lim=2;
for (reg int i=1;;i++)
{
t=(1LL*t*t+c)%n; val=1LL*val*abs(s-t)%n;
if (i==lim || !(i%127))
{
int d=gcd(n,val);
if (d>1) return d;
if (i==lim) s=t,val=1,lim<<=1;
}
}
return 19260817;
}
void PR(int n)
{
if (n<maxd || n<2) return;
if (MR(n)) { maxd=n; return; }
int p=work(n);
while (p>=n) p=work(n);
while (!(n%p)) n/=p;
PR(n); PR(p);
}
int main()
{
freopen("phi.in","r",stdin);
freopen("phi.out","w",stdout);
srand('Q'+'u'+'a'+'n'+'t'+'A'+'s'+'k'+'A'+'K'+'I'+'O'+'I');
findprm(40000);
getfac(40000);
scanf("%d%d",&n,&Q);
for (reg int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
seg.update(1,1,n,i,a[i]);
}
while (Q--)
{
int opt,l,r;
scanf("%d%d%d",&opt,&l,&r);
if (opt==0)
{
seg.update(1,1,n,l,r);
a[l]=r;
}
if (opt==1)
{
int sum=seg.query1(1,1,n,l,r),phi=sum;
for (reg int i=1;prm[i]<=1300;i++)
if (!(sum%prm[i]))
{
phi=phi/prm[i]*(prm[i]-1);
while (!(sum%prm[i])) sum/=prm[i];
}
if (sum>1)
{
maxd=0; PR(sum);
phi=phi/maxd*(maxd-1);
if (1LL*maxd*maxd!=sum && maxd!=sum)
phi=phi/(sum/maxd)*(sum/maxd-1);
}
printf("%d\n",phi);
}
if (opt==2)
{
g.reset();
int mul=seg.query2(1,1,n,l,r);
for (reg int i=1;i<M;i++)
if (g[i]) mul=1LL*mul*inv[i]%MOD*(prm[i]-1)%MOD;
printf("%d\n",mul);
}
}
return 0;
}