思路当然来自我们的京学长咯(膜拜巨佬)(代码也基本一样。。),这是他的博客,里面有很多有用的东西:https://mmfunnytree.github.io/2050/01/18/AlgorithmTemplate/#more
回到思路:因为题目要满足min(a[j],a[i])= gcd(a[j],a[i]),所以肯定有一个数是另外一个的因数
因为这是个区间问题,我首先想到线段树,但是单纯的线段树肯定不行,我们得建立a[i]和a[j]的关系
我们可以用 类似埃筛的方法 : 对于 j 这个数 如果它的倍数和它自己在查询区间L到R之间的话,那么对答案就有1贡献
所以 我们可以 利用主席树对于每个j的倍数(不包括j本身) 我们把j的位置的权值+1,当询问区间L到R时答案就是R和L-1这两颗线段树在区间L到R的 权值差之和(一颗树K维护的是从区间1到K的答案)
可能讲的有点稀巴烂,有什么错误请指出(555 太菜了)
#include<bits/stdc++.h>
/*
题意:求区间[l,r]内满足min(a[i],a[j])=gcd(a[i],a[j])(l<=i<j<=r)的对数
*/
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
vector<int> yz[maxn];
int n,m;
int a[maxn],pa[maxn];
int T[maxn],lson[maxn*200],rson[maxn*200];
int c[maxn*200];
int tot;
void update(int &u,int p,int l,int r){
++tot;lson[tot]=lson[u];rson[tot]=rson[u];c[tot]=c[u]+1;
u=tot;
if(l==r)return;
int mid=l+r>>1;
if(p<=mid)update(lson[u],p,l,mid);
else update(rson[u],p,mid+1,r);
}
int query(int lrt,int rrt,int L,int R,int l,int r){
if(L<=l&&R>=r){
return c[rrt] - c[lrt];
}int mid = l+r>>1,ret=0;
if(L<=mid)ret+=query(lson[lrt],lson[rrt],L,R,l,mid);
if(R>mid) ret+=query(rson[lrt],rson[rrt],L,R,mid+1,r);
return ret;
}
int main(){
++tot;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i){
scanf("%d",a+i);
pa[a[i]]=i;
}
for(int i = 1; i <= n; i++){
for(int j = 2*i; j <= n; j+=i){
yz[j].push_back(pa[i]);
}
}
for(int i=1;i<=n;++i){
T[i]=T[i-1];
for(auto v:yz[a[i]]){
update(T[i],v,1,n);
}
}
int l,r;
while(m--){
scanf("%d%d",&l,&r);
printf("%d\n",query(T[l-1],T[r],l,r,1,n));
}
return 0;
}