题目链接:传送门
思路:
考虑将每个数字分解,判断当前这个数字a[i]的质因子是否在前面出现过,如果在位置j出现过,就代表前面这个数字a[j]不能和当前数字a[i]在一个区间。我们这样统计出每个数字最远能向后到达的位置后的一个位置。
然后倍增更新这个位置,dp[i][j] = dp[ dp[i-1][j-1] ][j-1];
,在计算最终结果时,我们可以将大区间分割为小区间,求出所有区间能分割的数量。
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
int a[N],n,q,b[N],dp[N][33];
vector <int> vc[N];
int main(void)
{
cin>>n>>q;
//预处理出每个数字的质因子
for(int i=2;i<N;i++)
if(!vc[i].size()){
b[i] = n+1;
for(int j=i;j<N;j+=i)
vc[j].push_back(i);
}
for(int i=1;i<=n;i++) cin>>a[i];
//处理出每个数字能向后跳到多远,最远是n+1,就是跳不到
dp[n+1][0] = n+1;
for(int i=n;i>=1;i--){
dp[i][0] = dp[i+1][0];
for(auto it:vc[ a[i] ])
dp[i][0] = min(dp[i][0],b[it]),b[it] = i;
}
//倍增处理,相当于对每个位置i找到以2^j为距离单位最近能够找到哪个位置。
for(int j=1;j<=20;j++)
for(int i=1;i<=n+1;i++)
dp[i][j] = dp[ dp[i][j-1] ][j-1];
while(q--){
int l,r;cin>>l>>r;
int ans = 0;
//先对大区间进行查找,找到大区间查找,再对小区间进行查找
for(int i=20;i>=0;i--){
if(dp[l][i] <= r){
ans |= (1<<i);
l = dp[l][i];
}
}
cout<<(ans+1)<<endl;
}
return 0;
}