题解:
考虑如何求
n
n
n个数的
l
c
m
lcm
lcm,可以求出前
n
−
1
n-1
n−1个数的
l
c
m
=
x
lcm = x
lcm=x,再和
a
n
=
y
a_n =y
an=y求
l
c
m
=
x
y
g
c
d
(
x
,
y
)
lcm = \frac {xy}{gcd(x,y)}
lcm=gcd(x,y)xy
然后就可以python骗到50分了
x
y
gcd
(
x
,
y
)
=
a
n
gcd
(
x
,
y
)
x
\frac {xy}{\gcd(x,y)} = \frac {a_n}{\gcd(x,y)} x
gcd(x,y)xy=gcd(x,y)anx
x
x
x可以模。
考虑怎么在不用高精度的情况下计算
y
gcd
(
x
,
y
)
m
o
d
1000000007
\frac {y}{\gcd(x,y)} \mod 1000000007
gcd(x,y)ymod1000000007
gcd
(
x
,
y
)
=
gcd
(
x
m
o
d
y
,
y
)
\gcd(x,y) = \gcd(x \mod y , y)
gcd(x,y)=gcd(xmody,y)
所以我们只需要再算
x
m
o
d
y
x\mod y
xmody就行了。
可以
O
(
n
2
+
n
log
n
)
O(n^2+n\log n)
O(n2+nlogn)求出
n
n
n个数的
l
c
m
lcm
lcm
考虑多组询问。
我们考虑预处理出所有答案。
假设我们安排了在求
l
c
m
lcm
lcm中一个一个加入
a
i
a_i
ai的顺序,
那么放
a
i
a_i
ai进来的时候,可以直接令
b
i
=
a
i
gcd
(
a
i
,
x
)
b_i = \frac {a_i}{\gcd(a_i,x)}
bi=gcd(ai,x)ai
那么答案就相当于是
∏
b
i
\prod b_i
∏bi
对于所有的区间,我们都安排一个统一的顺序(从大编号到小编号加入),那么我们可以寻求到一些可以重复利用的计算信息。
从小标号到大插入
a
i
a_i
ai,然后对于每个
b
j
,
j
<
i
b_j,j<i
bj,j<i更新为
b
j
g
c
d
(
b
j
,
∏
k
=
j
+
1
i
b
k
)
\frac {b_j}{gcd(b_j,\prod _{k=j+1}^{i} b_k)}
gcd(bj,∏k=j+1ibk)bj,其实只需要看
a
i
a_i
ai就可以了。
然后(对于右端点是
i
i
i的)区间询问
[
l
,
i
]
[l,i]
[l,i]就是
∏
j
=
l
i
b
j
\prod_{j=l}^ib_j
∏j=libj
这个方法每次都要求
gcd
\gcd
gcd,是
O
(
n
2
log
n
)
O(n^2\log n)
O(n2logn),总复杂度是
O
(
T
n
2
log
n
)
O(Tn^2\log n)
O(Tn2logn)难以通过。
O(1)gcd需要O(值域)的预处理
考虑优化每次求
gcd
\gcd
gcd的过程。
一个数
a
i
a_i
ai和前面
i
−
1
i-1
i−1段
[
l
,
i
−
1
]
[l,i-1]
[l,i−1]分别的
gcd
\gcd
gcd只会有
O
(
log
a
)
O(\log a)
O(loga)种。
所以从小编号到大编号更新
b
j
b_j
bj,用模运算来快速判断
gcd
\gcd
gcd是否会变小(一段变短乘积变小)以此计算。
复杂度是
O
(
n
2
+
n
log
2
n
)
O(n^2 + n\log ^2n)
O(n2+nlog2n)可以通过本题。
#include<bits/stdc++.h>
#define maxn 305
#define mod 1000000007
#define LL long long
using namespace std;
char cb[1<<15],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<15,stdin),cs==ct)?0:*cs++)
template<class T>void read(T &res){
char ch;
for(;!isdigit(ch=getc()););
for(res=ch-'0';isdigit(ch=getc());res=res*10+ch-'0');
}
int n,q,ans[maxn][maxn];
LL a[maxn],b[maxn];
inline LL mul(LL a,LL b,LL p){
LL r = a*b-(LL)((long double)a/p*b+0.5)*p;
return r < 0 ? r + p : r;
}
inline LL gcd(LL a, LL b){
if(!a||!b)return a|b;
register int t=__builtin_ctzll(a|b);
a>>=__builtin_ctzll(a);
do{
b>>=__builtin_ctzll(b) ;
if(a>b){LL t=b;b=a,a=t;}
b-=a;
}while(b);
return a<<t;
}
int main(){
int T;
for(read(T);T--;){
read(n),read(q);
for(int i=1;i<=n;i++){
read(a[i]);
b[i] = 1;
for(int j=i-1;j>=1;j--) b[j] = mul(a[j],b[j+1],a[i]);
LL G = gcd(b[1] , a[i]) , t;
for(int j=1;j<i;j++)
if(t=(b[j+1]%G)){
t=gcd(t,G);
a[j] /= G / t , G = t;
}
ans[i][i] = a[i] % mod;
for(int j=i-1;j>=1;j--) ans[i][j] = 1ll * ans[i][j+1] * (a[j] % mod) % mod;
}
for(int l,r;q--;){
read(l),read(r);
printf("%d\n",(ans[r][l]+mod)%mod);
}
}
}