题意:
给定长度为n的序列,q次询问,
每次询问给出L,R,要求计算对于[L,R]的所有子区间,有多少种不同的区间gcd。
数据范围:n,q<=1e5,1<=a(i)<=1e6
解法:
先看这道题:
https://blog.csdn.net/weixin_44178736/article/details/115346410
与上面的题类似,先预处理出对于每个i,其作为右端点向左扩展,每种gcd出现的最右位置.
然后将询问离线,按r从小到大排序,
从左到右遍历,用树状数组维护每个位置出现了多少种gcd,每种gcd只保存最右位置,
对于在点i上的询问,计算[l,i]出现了多少种gcd即可.
code:
#include<bits/stdc++.h>
#define PI pair<int,int>
using namespace std;
const int maxm=1e6+5;
vector<PI>R[maxm];
int mark[maxm];
int ans[maxm];
int a[maxm];
int n,q;
struct QQ{
int l,r,id;
}Q[maxm];
struct BIT{
int c[maxm];
int lowbit(int i){
return i&-i;
}
void add(int i,int t){
while(i<maxm)c[i]+=t,i+=lowbit(i);
}
int ask(int i){
int ans=0;
while(i)ans+=c[i],i-=lowbit(i);
return ans;
}
void init(){
memset(c,0,sizeof c);
}
}T;
void init(){
for(int i=0;i<=n;i++)R[i].clear();
for(int i=1;i<=n;i++){
R[i].push_back({a[i],i});
int lastg=a[i];
for(auto j:R[i-1]){
int g=__gcd(lastg,j.first);
if(g!=lastg){
R[i].push_back({g,j.second});
lastg=g;
}
}
}
}
bool cmp(QQ a,QQ b){
return a.r<b.r;
}
signed main(){
ios::sync_with_stdio(0);
while(cin>>n>>q){
T.init();
for(int i=1;i<=n;i++){
cin>>a[i];
}
init();
for(int i=1;i<=q;i++){
int l,r;cin>>l>>r;
Q[i]={l,r,i};
}
sort(Q+1,Q+1+q,cmp);
memset(mark,0,sizeof mark);
int j=1;
for(int i=1;i<=n;i++){
for(auto p:R[i]){
if(p.second>mark[p.first]){
if(mark[p.first]){
T.add(mark[p.first],-1);
}
mark[p.first]=p.second;
T.add(mark[p.first],1);
}
}
while(j<=q&&Q[j].r==i){
ans[Q[j].id]=T.ask((int)1e6)-T.ask(Q[j].l-1);
j++;
}
}
for(int i=1;i<=q;i++){
cout<<ans[i]<<endl;
}
}
return 0;
}