Codeforces Round #717 (Div. 2)D. Cut
题意:给一个长为n的数组,q次询问,区间l到r间需要最少分成多少段,使每一段的gcd等于1。
题解:易知拥有同一质因子的数不能在一段中,考虑将每一个数质因数分解,再利用序列自动机可以求出每一个数前缀的最近的一个不合法的位置,很明显每个数只会连向它之前的一个位置,数组便形成了一颗树。再来看问题,求区间l至r需要分多少段,其实就是求r与l间的高度差,用倍增法求即可。
#include<iostream>
#include<vector>
#include<cmath>
using namespace std;
const int N=1e5+7;
int n,q,a[N];
int prime[N],num[N];
vector<int>ho[N];
int ans[N],f[N][30];
void solve() {
for(int i=2;i<sqrt(N);i++) {
if(!prime[i])prime[++prime[0]]=i;
for(int j=1;j<=prime[0]&&prime[j]<sqrt(N)/i;j++){
prime[prime[j]*i]=1;
if(i%prime[j]==0)break;
}
}
}
void add(int p,int z){
for(int i=1;i<=prime[0];i++){
if(z%prime[i]==0){
while(z%prime[i]==0){
z/=prime[i];
}
ho[p].push_back(prime[i]);
}
}
if(z!=1){
ho[p].push_back(z);
}
}
int main(){
solve();
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
add(i,a[i]);
}
for(int i=1;i<=n;i++){
f[i][0]=f[i-1][0];
for(int j=0;j<ho[i].size();j++){
int p=ho[i][j];
f[i][0]=max(f[i][0],num[p]);
num[p]=i;
}
}
for(int i=1;i<=20;i++){
for(int j=1;j<=n;j++){
f[j][i]=f[f[j][i-1]][i-1];
}
}
for(int i=1;i<=q;i++){
int l,r;
scanf("%d%d",&l,&r);
int x=0;
for(int j=20;j>=0;j--){
if(f[r][j]>=l){
x+=1<<j;
r=f[r][j];
}
}
cout<<x+1<<"\n";
}
}