题意:
长度n的序列, m个询问区间[L, R], 问区间内的所有子段的不同GCD值有多少种.
思路:
区间GCD收敛的很快,所以直接暴力预处理出到每个数字截至的后缀串有哪些GCD以及它们的位置,就是每个数字向前看有哪些GCD出现,这个数量是很少的。
1.枚举区间的右坐标,然后枚举出所有的以这个为右坐标为区间左坐标。
2.并求出他们这个连续区间的gcd,去重,(即重复的不再记录)
3.然后对区间以右坐标为基准进行枚举各个左坐标中对应的gcd,
(利用的性质有:1.右坐标为基准,从右向左枚举左坐标,对应的连续区间的gcd一定是不递增的性质;
2.右坐标为基准存的gcd不存在重复的情况,每一次枚举一个就可以看成是这个坐标位置上的个数加1,
所以可以利用树状数组进行区间查询与修改。
)
树状数组统计的原理类似
[1,1]
[2,2][1, 2]
[3,3][2, 3][1,2,3]
[4,4 ][3, 4][2,3,4][1,2,3,4]
前面统计的数量可以影响到后面的区间,有的影响是有必要的,有的是没有必要的。
例如 对于[1--4] 区间,应该是先计算[1,1]
再计算[1,2][2,2]->[1-3][2,3][3,3]->[1-4][2-4][3,4][4,4];
因为vec1[i]存的是以 i结尾的连续序列的gcd ,所以还需要依赖 vec1[i-1] 的结果,或者去重。
#include<bits/stdc++.h>
#define LL long long
#define bug puts("**************")
using namespace std;
const int N=110000;
int a[N];
int pre[N*10];
int sum[N];
int gcd(int a,int b){
return b==0?a:gcd(b,a%b);
}
int n,m;
vector<pair<int,int> > vec1[N],vec2[N];
///vec1[i] i为右边下标 first为左边下标 second 为 [j,i]连续区间元素的gcd
///vec2[i] i为右边下标 first为左边坐标 second为序号(第几个访问的)
int lowbit(int x){
return x&(-x);
}
void add(int x,int d){
while(x<=n){
sum[x]+=d;
x+=lowbit(x);
}
}
int getsum(int x){
int ans=0;
while(x){
ans+=sum[x];
x-=lowbit(x);
}
return ans;
}
int ans[N];
void solve(){
memset(sum,0,sizeof(sum));
memset(pre,0,sizeof(pre));
///此区间是逐渐增大的,并且是包含关系,所以可以用树状数组
for(int i=1;i<=n;i++){ ///从小到大 枚举右坐标,
int len=vec1[i].size();
for(int j=0;j<len;j++){
pair<int,int> tmp=vec1[i][j]; ///表示以i结尾的连续序列的gcd
if(pre[tmp.second]) add(pre[tmp.second],-1); ///去除以前统计过的,去重,(为了统计不同的个数)
pre[tmp.second]=tmp.first;
add(tmp.first,1);
}
len=vec2[i].size();
for(int j=0;j<len;j++){
pair<int,int>tmp=vec2[i][j];
///访问顺序 ///右坐标 左坐标
ans[tmp.second]=getsum(i)-getsum(tmp.first-1);
}
}
//bug;
for(int i=1;i<=m;i++){
printf("%d\n",ans[i]);
}
}
int main(){
while(~scanf("%d%d",&n,&m)){
memset(vec1,0,sizeof(vec1));
memset(vec2,0,sizeof(vec2));
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=1;i<=n;i++){
int pos=i;
int len=vec1[i-1].size();
int tmpval=a[i];
vec1[i].push_back(make_pair(i,a[i]));
for(int j=0;j<len;j++){
pair<int,int> tmp=vec1[i-1][j];
int Gcd=gcd(tmp.second,tmpval);
pos=tmp.first;
if(tmpval!=Gcd){
vec1[i].push_back(make_pair(pos,Gcd));
tmpval=Gcd;
}
}
}
for(int i=1;i<=m;i++){
int u,v;
scanf("%d%d",&u,&v);
vec2[v].push_back(make_pair(u,i));
}
// for(int i=1;i<=n;i++){
// // cout<<vec1[i].size();
// for(int j=0;j<vec1[i].size();j++){
// printf("%d ",vec1[i][j].second);
// }
// puts("");
// }
solve();
}
return 0;
}
/*******************************
*******************************/