写在前面:
看了看其他题解,居然都是线段树,于是便有了这篇文章
。。。
思路:
题目本质是求区间 GCD 并求区间数字是 GCD 的个数。
区间扩展时信息很好更新,但在缩小时信息难以更新,故考虑回滚莫队。
因为要记录数字个数,数据范围又是 1e9 区间 GCD 又不能离散化, 开 map ??? 那可以T飞了(在cf上测了下 7000ms)
现在区间只有扩展,GCD 只会不断减小 ,假如区间多了一个数 s ,区间 GCD 减小 , 可以证明原区间一定不含 s (反证法可以证明)
这样 map 就可以省了
变成回滚莫队板题了。。。
Code:
#include <iostream>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <algorithm>
#include <vector>
#include <string>
#include <iomanip>
#include <cmath>
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <climits>
//#include <unordered_map>
#define guo312 std::ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define ll long long
#define Inf LONG_LONG_MAX
#define inf INT_MAX
#define endl "\n"
#define PI 3.1415926535898
using namespace std;
const int N=1e5+10;
int a[N],bel[N],lk;
struct PW{
int l,r,id;
}b[N];
bool cmp(PW s1,PW s2){
if(bel[s1.l]!=bel[s2.l]){
return bel[s1.l]<bel[s2.l];
}
else{
return s1.r<s2.r;
}
}
int getans(int l,int r){
int now=0;
for(int i=l;i<=r;i++){
if(now==0) now=a[i];
else now=__gcd(a[i],now);
}
int re=0;
for(int i=l;i<=r;i++){
if(a[i]==now) re++;
}
return r-l+1-re;
}
int ans[N];
int main(){
guo312;
int n; cin>>n; lk=sqrt(n);
for(int i=1;i<=n;i++){
cin>>a[i];
bel[i]=(i-1)/lk+1;
}
int q; cin>>q;
for(int i=1;i<=q;i++){
cin>>b[i].l>>b[i].r;
b[i].id=i;
}
sort(b+1,b+1+q,cmp);
int now=0,last=0,l,r,now_num,last_num;
for(int i=1;i<=q;i++){
if(bel[b[i].l]==bel[b[i].r]){
ans[b[i].id]=getans(b[i].l,b[i].r);
continue;
}
l=bel[b[i].l]*lk;
if(bel[b[i].l]>bel[b[i-1].l]||bel[b[i-1].l]==bel[b[i-1].r]){
now=last=0;
now_num=last_num=0;
r=l-1;
}
now=last,now_num=last_num;
while(r<b[i].r){
r++;
if(now==0){
now_num++,now=a[r];
}
else{
int s=__gcd(now,a[r]);
if(s!=now) now=s,now_num=0;
if(now==a[r]) now_num++;
}
}
last=now,last_num=now_num;
while(l>b[i].l){
l--;
if(now==0){
now_num++,now=a[l];
}
else{
int s=__gcd(now,a[l]);
if(s!=now) now=s,now_num=0;
if(now==a[l]) now_num++;
}
}
ans[b[i].id]=b[i].r-b[i].l+1-now_num;
}
for(int i=1;i<=q;i++){
cout<<ans[i]<<endl;
}
return 0;
}