CodeForces 301D Yaroslav and Divisors (树状数组+离线查询)

题意:给你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,l1]的方案数,但这是不够的,因为可能存在一个数在 [ l , r ] [l,r] [l,r]中,另一个在 [ 1 , l − 1 ] [1,l-1] [1,l1]中的情况,我们记该种情况个数为 y y y,那么区间方案数就是 x − y x-y xy

接下来考虑如何更新 x x x y y y
用两个数组记录区间,一个 q l [ ] ql[] ql[]根据左端点排序,一个 q r [ ] qr[] qr[]根据右端点排序。
q l [ ] ql[] ql[]用来计算 y y ysum[ql[j].id] -= su(ql[j].r) - su(ql[j].l - 1);,因为基数在 l l l左边这应该不难理解。
q r [ ] qr[] qr[]用来计算 x x xsum[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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值