ICPC徐州网络赛I.query

题意:

题意好懂,给定一个序列,查询[L,R]之间有多少对

第一眼看到题马上想到Comet OJ#3 F 题“一模一样” 但是那道使用了二次离线莫队
非常高端找约数的题(当时就1个人过),但过了一会很多人过了。仔细一看原来序列不会有重复。。。zzzzz
比赛种陷入莫队的坑一发不可收拾(mdzz)
事后仔细想想就是个裸的二维偏序 (曾经归神给我们出过)

题解:

因为能产生贡献的数有所以题目就简化为给你一个区间查询里面产生贡献的区间有几个。
显然离线操作
把查询的和已知按$R$从小到大排序,对其贡献区间$R$小于查询的$L$打入树状数组。
然后只要查询有多少个$L$在$[q[i].l,q[i].r]$之间就行了

代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
const int M = 3e6 + 10;
struct node
{
int l, r, id;
} f[M], q[M];
bool cmp(node a, node b)
{
return a.r < b.r;
}
int n, m, tr[N], pos[N];
int lowbit(int x)
{
return (-x) & x;
}
void update(int x)
{
while (x <= n)
{
tr[x]++;
x += lowbit(x);
}
}
int quary(int x)
{
int sum = 0;
while (x)
{
sum += tr[x];
x -= lowbit(x);
}
return sum;
}
int cnt, ans[N];
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
{
int x;
scanf("%d", &x);
pos[x] = i;
}
for (int i = 1; i <= m; i++)
{
scanf("%d%d", &q[i].l, &q[i].r);
q[i].id = i;
}
for (int i = 1; i <= n; i++)
for (int j = 2; j * i <= n; j++)
{
f[++cnt].l = min(pos[i], pos[i * j]);
f[cnt].r = max(pos[i], pos[i * j]);
}
sort(1 + q, 1 + q + m, cmp);
sort(1 + f, f + 1 + cnt, cmp);
int num = 1;
for (int i = 1; i <= m; i++)
{
while (num <= cnt && f[num].r <= q[i].r)
update(f[num].l), num++;
ans[q[i].id] = quary(q[i].r) - quary(q[i].l - 1);
}
for (int i = 1; i <= m; i++)
printf("%d\n", ans[i]);
}