题目大意
给出一个长度为
n
n
n 的序列
a
a
a,以及
q
q
q 次询问
每次询问给出
l
,
r
l,r
l,r,问最少需要把区间
[
l
,
r
]
[l,r]
[l,r] 划分成多少段,满足每段内元素的 LCM 等于元素的乘积
这数据范围,这询问方式,一看就是DS题
首先,我们考虑 LCM 的性质。如果一段区间内的数的 LCM 等于所有元素之积,那么这个区间中的数一定两两互质。
我们设
n
x
t
i
nxt_i
nxti 表示
i
i
i 后面第一个与
a
i
a_i
ai 不互质的数的位置
同时,记
f
i
f_i
fi 为以
i
i
i 为左端点时,满足区间内数两两互质的最远右端点
+
1
+1
+1,也就是下一个互质区间的左端点
我们考虑从后往前计算
n
x
t
i
nxt_i
nxti
对于每个质数
p
k
p_k
pk,我们维护
c
k
c_k
ck 表示
p
k
p_k
pk 的倍数出现的最近位置
那么,
n
x
t
i
nxt_i
nxti 就是所有
a
i
a_i
ai 的质因数的
c
c
c 的最小值
那么显然 f i = min k = i n x t i − 1 n x t k f_i=\min\limits_{k=i}^{nxt_i-1}nxt_k fi=k=iminnxti−1nxtk,可用线段树解决
最后,我们考虑将 f i f_i fi 进行倍增。 f i , k f_{i,k} fi,k 表示 i i i 后面第 2 k 2^k 2k 个互质区间的左端点。最终在询问时,只需要尽可能地倍增向后跳,并统计答案即可。
时间复杂度
O
(
n
+
n
n
+
n
log
n
+
n
log
n
)
=
O
(
n
n
)
\mathcal O(n+ n\sqrt n + n \log n + n \log n)=\mathcal O(n\sqrt n)
O(n+nn+nlogn+nlogn)=O(nn)
一个小优化:我们可以用埃氏筛寻找质数,并预处理每个树的质因子。由于
1
0
5
10^5
105 内的数的不同质因子数不超过
6
6
6,所以计算
n
x
t
nxt
nxt 的复杂度变为
O
(
n
)
\mathcal O(n)
O(n),总复杂度
O
(
n
log
n
)
\mathcal O(n \log n)
O(nlogn)
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
const int Maxn=1e5+10;
const int Maxm=Maxn<<2;
const int inf=0x3f3f3f3f;
vector <int> c[Maxn];
int b[Maxn],a[Maxn];
int f[Maxn][30];
int p[Maxn],id[Maxn];
bool vis[Maxn];
int minv[Maxm];
int n,m,q,cnt;
int gcd(int x,int y)
{
if(!y)return x;
return gcd(y,x%y);
}
inline void push_up(int k)
{
minv[k]=min(minv[k<<1],minv[k<<1|1]);
}
void build(int k,int l,int r)
{
if(l==r)
{
minv[k]=f[l][0];
return;
}
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
push_up(k);
}
int query(int k,int l,int r,int x,int y)
{
if(x<=l && r<=y)return minv[k];
int mid=(l+r)>>1,ret=inf;
if(x<=mid)ret=query(k<<1,l,mid,x,y);
if(mid<y)ret=min(ret,query(k<<1|1,mid+1,r,x,y));
return ret;
}
inline int read()
{
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0' && ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
return s*w;
}
int main()
{
// freopen("in.txt","r",stdin);
n=read(),q=read();
for(int i=1;i<=n;++i)
a[i]=read(),m=max(m,a[i]);
vis[1]=1;
for(int i=2;i<=m;++i)
{
if(vis[i])continue;
b[++cnt]=i,id[i]=cnt;
c[i].push_back(cnt);
for(int j=(i<<1);j<=m;j+=i)
vis[j]=1,c[j].push_back(cnt);
}
for(int i=1;i<=cnt;++i)
p[i]=n+1;
fill(f[n+1],f[n+1]+25,n+1);
for(int i=n;i;--i)
{
f[i][0]=n+1;
for(int j=0;j<c[a[i]].size();++j)
{
f[i][0]=min(f[i][0],p[c[a[i]][j]]);
p[c[a[i]][j]]=i;
}
}
build(1,1,n);
for(int i=n;i;--i)
{
f[i][0]=query(1,1,n,i,f[i][0]-1);
for(int j=1;j<=20;++j)
f[i][j]=f[f[i][j-1]][j-1];
}
while(q--)
{
int l=read(),r=read();
int x=l,ret=0;
for(int i=20;i>=0;--i)
if(f[x][i]<=r)
ret+=(1<<i),x=f[x][i];
++ret;
printf("%d\n",ret);
}
return 0;
}