Description
给定一个长度为 (n) 的序列,有 (q) 组询问,每次询问给定一个下标区间 ([l,r]),问这个区间中,有多少数可以整除区间中的所有数。
Solution
对于区间 ([l,r]),(a_i) 整除所有数即 (a_i | extrm{gcd}(a_l,a_{l+1},...,a_r))
令 (g= extrm{gcd}(a_l,a_{l+1},...,a_r)),可以用线段树在 (O(log n)) 时间内求出
而 (a_i | g and a_i le g Rightarrow a_i = g),于是只需要统计区间中等于 (g) 的个数即可
对所有数字离散化后,对每种数用 vector
存储其出现位置,查询时在 vector
上二分即可
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1000005;
const int dbg = 0;
int n,q,t1,t2,t3,l,r;
int a[N];
int gcd(int x,int y)
{
return __gcd(x,y);
}
namespace seg
{
int a[N];
void build(int p,int l,int r,int *src)
{
if(l==r)
{
a[p]=src[l];
}
else
{
build(p*2,l,(l+r)/2,src);
build(p*2+1,(l+r)/2+1,r,src);
a[p]=gcd(a[p*2],a[p*2+1]);
}
}
int query(int p,int l,int r,int ql,int qr)
{
if(l>qr || r<ql) return 0;
if(l>=ql && r<=qr) return a[p];
return gcd(query(p*2,l,(l+r)/2,ql,qr), query(p*2+1,(l+r)/2+1,r,ql,qr));
}
}
namespace cnter
{
map <int,int> mp;
int imp[N];
int ind=0;
vector <int> vec[N];
void init()
{
for(int i=1;i<=n;i++) mp[a[i]]++;
for(auto &i:mp) i.second=++ind, imp[ind]=i.first;
for(int i=1;i<=n;i++) vec[mp[a[i]]].push_back(i);
}
int query(int l,int r,int key)
{
int pos=mp[key];
if(pos)
{
vector <int> &v = vec[pos];
auto it1=lower_bound(v.begin(),v.end(),l);
auto it2=lower_bound(v.begin(),v.end(),r+1);
return it2-it1;
}
else return 0;
}
}
signed main()
{
ios::sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
seg::build(1,1,n,a);
cnter::init();
cin>>q;
for(int i=1;i<=q;i++)
{
cin>>l>>r;
int g=seg::query(1,1,n,l,r);
if(dbg) cout<<" g = "<<g<<endl;
int ans=cnter::query(l,r,g);
cout<<r-l+1-ans<<endl;
}
}