题目
题目链接:https://codeforces.com/problemset/problem/594/D
给出一个长度为 \(n\) 的数列,\(Q\) 次询问,每次询问给出 \(l,r\),需要求出 \(\varphi(\prod ^{r}_{i-l}a[i])\bmod p\)。
\(n,Q\leq 200000,a[i]\leq 10^6\)。
思路
考虑到一个数 \(x\) 的质因子不会超过 \(\log x\) 个,所以可以离线按照询问的右端点排序,记录每一个质因子最后出现的位置。
按照右端点排序后,我们对于一个询问 \([l_i,r_i]\),将 \((r_{i-1},r_i]\) 的数分别求质因子,然后找到上一个拥有这个质因子的数,用一个数据结构维护一下,保证对于每一个质因子 \(p\),它的贡献都算在现在询问到的区间的最后一个含有 \(p\) 质因子的数字上。而一个数 \(p\) 的贡献是 \(\frac{p-1}{p}\),所以我们需要一个支持单调修改、区间查询乘积的数据结构,线段树和树状数组都可以的。
如何快速处理出一个数的质因子?在线性筛的时候,我们求出了每个数的最小质因子 \(v\),那么就不断让 \(a[i]\) 除以 \(v[a[i]]\) 即可。
讲的似乎很模糊,看一下这部分代码
for (;j<=ask[i].r;j++)
for (int k=a[j];k>1;)
{
int d=v[k]; //求这一位的质因子
if (last[d]) seg.update(1,last[d],d);
if (last[d]) seg.update(1,last[d],inv[d-1]); //在线段树中将上一个含有这个质因子的位置的贡献消除
last[d]=j;
seg.update(1,j,inv[d]);
seg.update(1,j,d-1); //维护该质因子的贡献
while (k%d==0) k/=d;
}
那么整个问题就迎刃而解了。需要预处理逆元,否则时间复杂度多一个 \(\log n\)。
时间复杂度 \(O(n\log n\log a[i])\)。
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=200010,M=1e6+10,MOD=1e9+7;
int n,Q,m,a[N],last[M],v[M],prm[M],inv[M],ans[N];
struct Query
{
int l,r,id;
}ask[N];
bool cmp(Query x,Query y)
{
return x.r<y.r;
}
void findprm(int k)
{
for (int i=2;i<=k;i++)
{
if (!v[i]){v[i]=i; prm[++m]=i;}
for (int j=1;j<=m;j++)
{
if (1LL*prm[j]*i>k || v[i]<prm[j]) break;
v[prm[j]*i]=prm[j];
}
}
}
ll fpow(ll x,ll k,ll p)
{
ll ans=1;
for (;k;k>>=1,x=x*x%p)
if (k&1) ans=ans*x%p;
return ans;
}
struct Seg
{
int l[N*4],r[N*4];
ll mul[N*4];
void pushup(int x)
{
mul[x]=mul[x*2]*mul[x*2+1]%MOD;
}
void build(int x,int ql,int qr)
{
l[x]=ql; r[x]=qr;
if (ql==qr)
{
mul[x]=a[ql];
return;
}
int mid=(ql+qr)>>1;
build(x*2,ql,mid); build(x*2+1,mid+1,qr);
pushup(x);
}
void update(int x,int k,int val)
{
if (l[x]==k && r[x]==k)
{
mul[x]=mul[x]*val%MOD;
return;
}
int mid=(l[x]+r[x])>>1;
if (k<=mid) update(x*2,k,val);
else update(x*2+1,k,val);
pushup(x);
}
ll query(int x,int ql,int qr)
{
if (l[x]==ql && r[x]==qr)
return mul[x];
int mid=(l[x]+r[x])>>1;
if (qr<=mid) return query(x*2,ql,qr);
else if (ql>mid) return query(x*2+1,ql,qr);
else return query(x*2,ql,mid)*query(x*2+1,mid+1,qr)%MOD;
}
}seg;
int main()
{
findprm(M-10);
inv[0]=1;
for (int i=1;i<=M-10;i++)
inv[i]=fpow(i,MOD-2,MOD);
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%d",&a[i]);
seg.build(1,1,n);
scanf("%d",&Q);
for (int i=1;i<=Q;i++)
{
scanf("%d%d",&ask[i].l,&ask[i].r);
ask[i].id=i;
}
sort(ask+1,ask+1+Q,cmp);
for (int i=1,j=1;i<=Q;i++)
{
for (;j<=ask[i].r;j++)
for (int k=a[j];k>1;)
{
int d=v[k];
if (last[d]) seg.update(1,last[d],d);
if (last[d]) seg.update(1,last[d],inv[d-1]);
last[d]=j;
seg.update(1,j,inv[d]);
seg.update(1,j,d-1);
while (k%d==0) k/=d;
}
ans[ask[i].id]=seg.query(1,ask[i].l,ask[i].r);
}
for (int i=1;i<=Q;i++)
printf("%d\n",ans[i]);
return 0;
}