题目大意:
给你n个数,然后问你区间内有几对数a,b(b%a=0)。
解决方法:
将所有的区间都进行排序(按照左端点的大小),建立一个pre【i】数组,它表示i上一次出现在某个位置,pre【i】初始值为0.
从右向左处理每个数字,将每个数字的因子与倍数k都求出来,然后树状数组进行add(pre[k],1)的操作。某段区间的答案就是sum(右端点)。
为什么呢?想想如果k是i的因子或者倍数,i在pos1处,k在pos2处,那么所有区间包含pos1与pos2的都要加上1,我们又是左端点排序,所有直接求sum(右端点)就行了。
我的代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define maxn 1000000
using namespace std;
int pre[maxn];
long long tree[maxn];
struct Point {
int x,y,id;
long long ans;
}po[maxn];
int num[maxn];
int n,m;
int maxx;
int add(int x,long long v){
for (int i=x;i<=n;i+=i&(-i))
tree[i]+=v;
return 0;
}
long long sum(int x){
long long ans=0;
for (int i=x;i>0;i-=i&(-i))
ans+=tree[i];
return ans;
}
int cmp(Point a,Point b){
return a.y<b.y||a.y==b.y&&a.x<b.x||a.y==b.y&&a.x==b.x&&a.id<b.id;
}
int cmp1(Point a,Point b){
return a.id<b.id;
}
int change(int n){
for (int i=num[n]*2;i<=maxx;i+=num[n]){
if (pre[i]) add(pre[i],1);
}
int k=(int)sqrt(num[n]);
//cout<<"num "<<num[n]<<endl;
for (int i=1;i<=k;i++)
if(num[n]%i==0){
//cout<<"*****"<<endl;
if (i==k){
if (k*k==num[n]){
// cout<<" i "<<i<<endl;
if (pre[i]) add(pre[i],1);
}
else {
// cout<<" i "<<i<<endl;
// cout<<" i "<<num[n]/i<<endl;
if (pre[i]) add(pre[i],1);
if (pre[num[n]/i]) add(pre[num[n]/i],1);
}
}
else {
// cout<<" i "<<i<<endl;
// cout<<" i "<<num[n]/i<<endl;
if (pre[i]) add(pre[i],1);
if (pre[num[n]/i]) add(pre[num[n]/i],1);
}
}
add(n,1);
pre[num[n]]=n;
return 0;
}
int main (){
//freopen("test.in","r",stdin);
while (~scanf("%d%d",&n,&m)){
memset(tree,0,sizeof(tree));
for (int i=1;i<=n;i++) scanf("%d",&num[i]),pre[i]=0;
for (int i=1;i<=m;i++){
scanf("%d%d",&po[i].x,&po[i].y);
po[i].id=i;
}
sort(po+1,po+m+1,cmp);
int pos=1;
maxx=0;
for (int i=1;i<=m;i++){
while (pos<=po[i].y&&pos<=n){
change(pos);
maxx=max(maxx,num[pos]);
pos++;
}
if (po[i].x>1)po[i].ans=sum(po[i].y)-sum(po[i].x-1);
else po[i].ans=sum(po[i].y);
}
sort(po+1,po+m+1,cmp1);
for (int i=1;i<=m;i++)
printf("%I64d\n",po[i].ans);
}
return 0;
}