给出一个长度为 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
#include
#include
#include
#include
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;
}