这是一篇题解
题目传送门
题目大意
找出 [ l , r ] [l, r] [l,r]中满足 a i a_i ai是 a j a_j aj的倍数关系的 ( i , j ) (i, j) (i,j)的对数
细节1 由于 每个数互不相同且数字不会超过 n n n , 因此一定是 1 ⋯ n 1\cdots n 1⋯n的排列
思路
1.记录每个数后面和他能组成倍数关系的数
枚举 1 ⋯ n 1\cdots n 1⋯n, 枚举出 a i a_i ai后面的所有倍数, 并用一个动态数组来存储这些倍数
for (int i = 1; i <= n; i++) {
for (int j = a[i]; j <= n; j += a[i])
if (id[j] != 0)
g[min(i, id[j])].push_back(max(i, id[j]));
}
2.计算答案
先用一个结构体来存储每一个输入的 [ l , r ] [l, r] [l,r]并记录下每一个输入的编号
结构体
bool cmp(Node x, Node y) {
return x.l > y.l;
}
细节2 由于后面是逆序遍历所以每个 l l l也要从大到小排列
输入
for (int i = 1; i <= m; i++) {
cin >> q[i].l >> q[i].r;
q[i].id = i;
}
接下来将 i i i后能与 i i i构成倍数关系的数计入树状数组,这样树状数组就能记录 1 ⋯ n 1\cdots n 1⋯n中满足 a j a_j aj是 a i a_i ai倍数的数量
for (int i = n; i >= 1; i--) {
for (int j = 0; j < g[i].size(); j++) add(g[i][j], 1);
细节3 因为我们是用一个数组记录是 a i a_i ai后面的所有倍数,所以要逆序遍历, 这样才能使add不重复
最后对于每次 i = = l i == l i==l的询问, 直接计算 [ l , r ] [l, r] [l,r]区间内的所有倍数的数量和
while (i == q[t].l) {
ans[q[t].id] = qry(q[t].r) - qry(q[t].l - 1);
t++;
}
细节4 因为是逆序遍历所以可以减去 q r y ( q [ t ] . l − 1 ) qry(q[t].l - 1) qry(q[t].l−1), 也可以不减去 q r y ( q [ t ] . l − 1 ) qry(q[t].l - 1) qry(q[t].l−1)
代码
#include <bits/stdc++.h>
#define ll long long
#define lowbit(x) x & (-x)
using namespace std;
const int N = 2 * 1e5 + 5;
struct Node{
int l, r, id;
} q[N];
bool cmp(Node x, Node y) {
return x.l > y.l;
}
int n, m, a[N], id[N];
ll c[N], ans[N];
vector<int> g[N];
void add(int x, int k) {
while (x <= n) {
c[x] += k;
x += lowbit(x);
}
}
ll qry(int x) {
ll sum = 0;
while (x) {
sum += c[x];
x -= lowbit(x);
}
return sum;
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
id[a[i]] = i;
}
for (int i = 1; i <= m; i++) {
cin >> q[i].l >> q[i].r;
q[i].id = i;
}
sort(q + 1, q + 1 + m, cmp);
for (int i = 1; i <= n; i++) {
for (int j = a[i]; j <= n; j += a[i])
if (id[j] != 0)
g[min(i, id[j])].push_back(max(i, id[j]));
}
int t = 1;
for (int i = n; i >= 1; i--) {
for (int j = 0; j < g[i].size(); j++) add(g[i][j], 1);
while (i == q[t].l) {
ans[q[t].id] = qry(q[t].r) - qry(q[t].l - 1);
t++;
}
}
for (int i = 1; i <= m; i++) cout << ans[i] << '\n';
return 0;
}
无注释放心复制
总结
人生第一篇题解终于水灵灵写完了, 如果有问题马上私信我