题意:给你n个不同的数,m次询问区间l到r,问有多少对,其中一个数是另一个数的因子。
题解:树状数组+离线查询
这个题很重要的一点就是自身与自身也算一个对。
在线不是很好弄,我们离线处理,根据区间的变化更新区间内对数。
如何计算区间方案数呢?
我们考虑区间
[
l
,
r
]
[l,r]
[l,r]的方案数,记
x
=
[
1
,
r
]
x=[1,r]
x=[1,r]的方案数 -
[
1
,
l
−
1
]
[1,l-1]
[1,l−1]的方案数,但这是不够的,因为可能存在一个数在
[
l
,
r
]
[l,r]
[l,r]中,另一个在
[
1
,
l
−
1
]
[1,l-1]
[1,l−1]中的情况,我们记该种情况个数为
y
y
y,那么区间方案数就是
x
−
y
x-y
x−y。
接下来考虑如何更新
x
x
x和
y
y
y。
用两个数组记录区间,一个
q
l
[
]
ql[]
ql[]根据左端点排序,一个
q
r
[
]
qr[]
qr[]根据右端点排序。
q
l
[
]
ql[]
ql[]用来计算
y
y
y,sum[ql[j].id] -= su(ql[j].r) - su(ql[j].l - 1);
,因为基数在
l
l
l左边这应该不难理解。
q
r
[
]
qr[]
qr[]用来计算
x
x
x,sum[qr[k].id] += su(qr[k].r) - su(qr[k].l - 1);
。
因为这题自身与自身也算一对,所以每个数及其倍数只要更新一次。
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<fstream>
#include<set>
#include<map>
#include<sstream>
#include<iomanip>
#define ll long long
using namespace std;
const int maxn = 2e5 + 5;
int n, m, a[maxn];
struct node {
int l, r, id;
}ql[maxn], qr[maxn];
ll tree[maxn];
void add(int x, int num) {
for (; x <= n; x += x & -x)
tree[x] += num;;
}
int su(int x) {
ll answer = 0;
for (; x > 0; x -= x & -x)
answer += tree[x];
return answer;
}
bool cmp1(node& x, node& y) {
return x.l == y.l ? x.r < y.r : x.l < y.l;
}
bool cmp2(node& x, node& y) {
return x.r == y.r ? x.l < y.l : x.r < y.r;
}
int vis[maxn];
int sum[maxn];
int main() {
int ma = 0;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]), vis[a[i]] = i, ma = max(ma, a[i]);
for (int i = 1; i <= m; i++) scanf("%d%d", &ql[i].l, &ql[i].r), qr[i].l = ql[i].l, qr[i].r = ql[i].r, ql[i].id = qr[i].id = i;
sort(ql + 1, ql + m + 1, cmp1);
sort(qr + 1, qr + m + 1, cmp2);
for (int i = 1, j = 1, k = 1; i <= n; i++) {
while (j <= m && ql[j].l == i) {
sum[ql[j].id] -= su(ql[j].r) - su(ql[j].l - 1);
j++;
}
//cout << sum[1] << " ";
for (int x = a[i]; x <= ma; x += a[i]) {
if (vis[x]) add(vis[x], 1);
}
while (k <= m && qr[k].r == i) {
sum[qr[k].id] += su(qr[k].r) - su(qr[k].l - 1);
k++;
}
//cout << sum[1] << endl;
}
for (int i = 1; i <= m; i++) printf("%d\n", sum[i]);
return 0;
}