Yaroslav and Divisors题解

这是一篇题解

题目传送门

洛谷
codeforces

题目大意

找出 [ 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 1n的排列

思路

1.记录每个数后面和他能组成倍数关系的数

枚举 1 ⋯ n 1\cdots n 1n, 枚举出 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 1n中满足 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].l1), 也可以不减去 q r y ( q [ t ] . l − 1 ) qry(q[t].l - 1) qry(q[t].l1)

代码

#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;
}
无注释放心复制

总结

人生第一篇题解终于水灵灵写完了, 如果有问题马上私信我

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值