Description
dC 在秒了BZOJ 上所有的数论题后,感觉萌萌哒,想出了这么一道水题,来拯救日益枯
竭的水题资源。
给定一个长度为 n的正整数序列A,有q次询问,每次询问一段区间内所有元素乘积的
φ(φ(n)代表1~n 中与n互质的数的个数) 。由于答案可能很大,所以请对答案 mod 10^6 +
777。 (本题强制在线,所有询问操作的l,r都需要 xor上一次询问的答案 lastans,初始时,
lastans = 0)
Input
第一行,两个正整数,N,Q,表示序列的长度和询问的个数。
第二行有N 个正整数,第i个表示Ai.
下面Q行,每行两个正整数,l r,表示询问[l ^ lastans,r ^ lastans]内所有元素乘积的φ
Output
Q行,对于每个询问输出一个整数。
Sample Input
5 10
3 7 10 10 5
3 4
42 44
241 242
14 9
1201 1201
0 6
245 245
7 7
6 1
1203 1203
Sample Output
40
240
12
1200
2
240
4
4
1200
4
HINT
1 <= N <= 50000
1 <= Q <= 100000
1 <= Ai <= 10^6
Source
orz Loidc
分析:
我们对所有数进行质因数分解。
设
a
n
s
=
∏
i
=
l
r
a
i
ans=\prod_{i=l}^{r}a_i
ans=∏i=lrai。对于一个质数
p
p
p,如果它出现过,那么就
a
n
s
∗
=
p
−
1
p
ans*=\frac{p-1}{p}
ans∗=pp−1。这个应该很显然。
前面这个可以直接前缀积,考虑后面这些要怎么求。
考虑怎么用可持久化线段树求区间不同元素的个数。
我们设
f
[
i
]
f[i]
f[i]表示有边界为
r
r
r时,左边界为
i
i
i的答案。当我们枚举到
r
+
1
r+1
r+1时,假设
k
k
k为
a
i
+
1
a_{i+1}
ai+1上一次出现的位置,那么
f
[
k
+
1
]
f[k+1]
f[k+1]到
f
[
i
+
1
]
f[i+1]
f[i+1]的答案都加了1。
所以我们要可持久化,把右边界为
1
1
1~
n
n
n都记录下来,考虑差分把区间修改单点查询变为单点修改区间查询即可。
对于本题,每个质数的贡献变为了
p
−
1
p
\frac{p-1}{p}
pp−1,而且是乘起来,那么我们可以在
k
+
1
k+1
k+1处乘
p
−
1
p
\frac{p-1}{p}
pp−1,在
i
+
2
i+2
i+2处乘
p
p
−
1
\frac{p}{p-1}
p−1p即可。
注意初值要为1,如果不一开始开一棵线段树的话要注意特判。
7777777
代码:
/**************************************************************
Problem: 4026
User: liangzihao
Language: C++
Result: Accepted
Time:5772 ms
Memory:174752 kb
****************************************************************/
#include <iostream>
#include <cstdio>
#include <cmath>
#define LL long long
const int maxn=5e4+7;
const int maxp=1e6+7;
const LL mod=1e6+777;
using namespace std;
int n,m,x,y,tot,sc,cnt;
int a[maxn],num[maxn],last[maxp],root[maxn*20];
int not_prime[maxn],prime[maxn];
LL prod[maxp],pa[maxn],pb[maxn],ans;
struct node{
int l,r;
LL data;
}t[maxn*200];
LL power(LL x,LL y)
{
if (y==0) return 1;
if (y==1) return x;
LL c=power(x,y/2);
c=(c*c)%mod;
if (y&1) c=(c*x)%mod;
return c;
}
void getprime(int n)
{
for (int i=2;i<=n;i++)
{
if (!not_prime[i]) prime[++tot]=i;
for (int j=1;j<=tot;j++)
{
if (i*prime[j]>n) break;
not_prime[i*prime[j]]=1;
if (i%prime[j]==0) break;
}
}
}
void ins(int &p,int q,int l,int r,int x,LL k)
{
if (!p) p=++cnt;
if (!t[q].data) t[p].data=k;
else t[p].data=t[q].data*k%mod;
if (l==r) return;
int mid=(l+r)/2;
if (x<=mid) t[p].r=t[q].r,ins(t[p].l,t[q].l,l,mid,x,k);
else t[p].l=t[q].l,ins(t[p].r,t[q].r,mid+1,r,x,k);
}
LL getprd(int p,int l,int r,int x,int y)
{
if ((l==x) && (r==y))
{
if (!t[p].data) return 1;
return t[p].data;
}
int mid=(l+r)/2;
if (y<=mid) return getprd(t[p].l,l,mid,x,y);
else if (x>mid) return getprd(t[p].r,mid+1,r,x,y);
else return getprd(t[p].l,l,mid,x,mid)*getprd(t[p].r,mid+1,r,mid+1,y)%mod;
}
int main()
{
scanf("%d%d",&n,&m);
prod[0]=1;
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
prod[i]=prod[i-1]*(LL)a[i]%mod;
}
getprime(1e3);
for (int i=1;i<=tot;i++)
{
pa[i]=((LL)prime[i]-1)*power((LL)prime[i],mod-2)%mod;
pb[i]=power(pa[i],mod-2);
}
for (int i=1;i<=n;i++)
{
int x=a[i];
for (int j=1;j<=tot;j++)
{
if (x%prime[j]==0)
{
ins(root[++sc],root[sc],1,n,last[prime[j]]+1,pa[j]);
if (i<n) ins(root[++sc],root[sc],1,n,i+1,pb[j]);
while (x%prime[j]==0) x/=prime[j];
last[prime[j]]=i;
}
}
if (x>1)
{
LL pA=((LL)x-1)*power((LL)x,mod-2)%mod;
LL pB=power(pA,mod-2);
ins(root[++sc],root[sc],1,n,last[x]+1,pA);
if (i<n) ins(root[++sc],root[sc],1,n,i+1,pB);
last[x]=i;
}
num[i]=sc;
}
for (int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
x^=ans,y^=ans;
ans=prod[y]*power(prod[x-1],mod-2)%mod;
ans=ans*getprd(root[num[y]],1,n,1,x)%mod;
printf("%lld\n",ans);
}
}