题目描述
题解
思维难度好大的题…
我们思考如何对于 a [ i ] a[i] a[i]来说,有多少对合法的 ( a [ i ] , a [ j ] ) ( j < i ) (a[i],a[j])(j<i) (a[i],a[j])(j<i).
我们可以预处理出所有a[i]的因数和倍数,并存储来一个数组内;当求解a[i]时,我们就把所有位置小于等于i(不计算大于是为了避免重复计算)的所有 a [ i ] a[i] a[i]的因数或倍数都存进树状数组里。如果要求解区间 [ L , R ] [L,R] [L,R],只要 a s k ( R ) − a s k ( L ) ask(R)-ask(L) ask(R)−ask(L)即可。
同理,我们需要 [ L , R ] [L,R] [L,R]中所有的对数,只需要 a s k ( R ) − a s k ( L ) ask(R)-ask(L) ask(R)−ask(L)即可;因为每一个数事实上是单独计算的,仅仅利用树状数组来维护一个整体累加。这样,我们就能够在枚举i的时候求解任意 q [ k ] = r q[k]=r q[k]=r的答案了。
最后,我们只要将每一个询问离线一下,按照右端点从左到右排序即可。
代码如下:
#include <bits/stdc++.h>
using namespace std;
int n,m;
int a[300000];
int pos[300000];
int ans[300000];
vector <int> big[300000];
vector <int> sma[300000];
struct node
{
int l, r, id;
friend bool operator < (node p1, node p2) {
return p1.r < p2.r;
}
};
node q[300000];
struct Tree
{
int S[1000000];
#define lowbit(i) (i&-i)
void add(int x,int v)
{
for (int i=x;i<=n;i+=lowbit(i))
S[i] += v;
return;
}
int ask(int x)
{
int sum = 0;
for (int i=x;i>=1;i-=lowbit(i))
sum += S[i];
return sum;
}
} tree;
inline int read(void)
{
int s = 0, w = 1; char c = getchar();
while (c < '0' || c > '9') c = getchar();
while (c >= '0' && c <= '9') s = s*10+c-48, c = getchar();
return s * w;
}
void Get(int x)
{
for (int i=x;i<=n;i+=x)
{
if (i != x) big[x].push_back(i);
sma[i].push_back(x);
}
return;
}
int main(void)
{
freopen("divisor.in","r",stdin);
freopen("divisor.out","w",stdout);
n =read(), m = read();
for (int i=1;i<=n;++i) a[i] = read(), pos[a[i]] = i;
for (int i=1;i<=m;++i) q[i].l = read(), q[i].r = read(), q[i].id = i;
sort(q+1, q+m+1);
for (int i=1;i<=n;++i) Get(i);
int now = 1;
for (int i=1;i<=n;++i)
{
for (int j=0;j<big[a[i]].size();++j)
{
int num = big[a[i]][j];
if (pos[num] <= i) tree.add(pos[num],1);
}
for (int j=0;j<sma[a[i]].size();++j)
{
int num = sma[a[i]][j];
if (pos[num] <= i) tree.add(pos[num],1);
}
while (q[now].r == i && now <= m)
ans[q[now].id] = tree.ask(q[now].r)-tree.ask(q[now].l-1), now ++;
}
for (int i=1;i<=m;++i) printf("%d\n", ans[i]);
return 0;
}