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

题意:给你n个不一样的数,m次询问区间l到r,问有多少对,其中一个数是另外一个数的因子。html

题解:树状数组+离线查询
这个题很重要的一点就是自身与自身也算一个对。node

在线不是很好弄,咱们离线处理,根据区间的变化更新区间内对数。ios

如何计算区间方案数呢?
咱们考虑区间 [ l , r ] [l,r] 的方案数,记 x = [ 1 , r ] x=[1,r] 的方案数 - [ 1 , l 1 ] [1,l-1] 的方案数,但这是不够的,由于可能存在一个数在 [ l , r ] [l,r] 中,另外一个在 [ 1 , l 1 ] [1,l-1] 中的状况,咱们记该种状况个数为 y y ,那么区间方案数就是 x y x-y web

接下来考虑如何更新 x x y y
用两个数组记录区间,一个 q l [ ] ql[] 根据左端点排序,一个 q r [ ] qr[] 根据右端点排序。
q l [ ] ql[] 用来计算 y y sum[ql[j].id] -= su(ql[j].r) - su(ql[j].l - 1);,由于基数在 l l 左边这应该不难理解。
q r [ ] qr[] 用来计算 x x sum[qr[k].id] += su(qr[k].r) - su(qr[k].l - 1);数组

由于这题自身与自身也算一对,因此每一个数及其倍数只要更新一次。app

#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;
}