题目大意:
给定一个序列,每一次询问[l,r]所有的子区间内物品加减可以组成的最小价值的种类。
思路:
不难发现最小价值就是一个区间的gcd(可以想一下exgcd是怎么求的,就知道了)。
然后可以
O(n2)
O
(
n
2
)
处理出来所有的区间的gcd,把一个区间gcd看成一种颜色,然后直接用树状数组数颜色。
但是
n2
n
2
显然有问题,不难发现固定了右端点之后,可能不同的右端点为了同一个颜色处理同一个左端点很多次,也就是以l开始的gcd可能在一段区间内相同,但是每一个都被处理了一次。
于是考虑这种重复的颜色我们只记录它的右端点最靠左的位置,也就是gcd第一次改变的位置,这样可以保证答案的正确性,由于gcd只有可能会有logn种,也可以保证复杂度。
至于计算gcd第一次改变的位置,可以用二分或者st表。
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define pii pair<int,int>
#define fi first
#define se second
#define mk make_pair
typedef long long ll;
using namespace std;
void File(){
freopen("hdu5869.in","r",stdin);
freopen("hdu5869.out","w",stdout);
}
template<typename T>void read(T &_){
T __=0,mul=1; char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')mul=-1;
ch=getchar();
}
while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
_=__*mul;
}
const int maxn=1e5+10;
const int maxa=1e6+10;
int n,q,a[maxn],g[maxn][25],pos[maxa],sum[maxn],ans[maxn];
vector<pii>b[maxn],c[maxn];
bool init(){
if(scanf("%d%d",&n,&q)==EOF)return false;
REP(i,1,n)read(a[i]);
REP(j,0,20){
int len=1<<j;
REP(i,1,n){
if(i+len-1>n)break;
if(len==1)g[i][j]=a[i];
else g[i][j]=__gcd(g[i][j-1],g[i+len/2][j-1]);
}
}
REP(i,1,n){
int p=i,val=g[i][0];
while(p<=n){
val=__gcd(val,a[p]);
b[p].push_back(mk(i,val));
//cout<<i<<" "<<p<<" "<<val<<endl;
int cnt=log(n-p+1)/log(2);
for(;cnt>=0;--cnt){
if(p+(1<<cnt)-1>n)continue;
if(g[p][cnt]%val==0)
p=p+(1<<cnt)-1;
}
++p;
}
}
return true;
}
int lowbit(int x){return x&(-x);}
void add(int p,int x){
if(!p)return;
while(p<=n){
sum[p]+=x;
p+=lowbit(p);
}
}
int query(int p){
int ret=0;
while(p>=1){
ret+=sum[p];
p-=lowbit(p);
}
return ret;
}
void work(){
int l,r;
REP(i,1,q){
read(l); read(r);
c[r].push_back(mk(l,i));
}
REP(i,1,n){
for(int sz=b[i].size()-1,j=0;j<=sz;++j){
int p=b[i][j].fi,val=b[i][j].se;
if(pos[val]<p){
add(pos[val],-1);
add(p,1);
pos[val]=p;
}
}
for(int sz=c[i].size()-1,j=0;j<=sz;++j)
ans[c[i][j].se]=query(i)-query(c[i][j].fi-1);
}
REP(i,1,q)printf("%d\n",ans[i]);
}
int main(){
File();
while(init()){
work();
memset(pos,0,sizeof(pos));
memset(sum,0,sizeof(sum));
REP(i,1,n)b[i].clear();
REP(i,1,n)c[i].clear();
}
return 0;
}