「Ynoi2018」GOSICK

不知道哪个阴间搬题人,陆续给模拟赛里整了第二分块、第七分块、第十四分块。

第二分块之前写的空间垃圾时间垃圾然后懒得搞了。

第七分块并没有看懂题解。

于是就只能写写这个了。

卡常卡不过别人的主要原因是懒(确信)

Idea:lxl Solution:lxl Std:lxl Data:lxl

“点缀光辉的第十四分块” (妈呀这名字好中二啊(谁叫我要用日本轻小说中的东西命名真是作死))

这题是我研究区间逆序对之后,枚举了一些区间pair贡献的题里面的一个

对这题的评价:8/11

—— lxl 博客

Luogu 5398

CometOJ contest#3 F

题意简述

给定长为 $n$ 的正整数序列 $\langle a\rangle$,$m$ 次询问给一个区间 $[l, r]$。

查询 $l\le i, j\le r$,且 $a_i$ 是 $a_j$ 的倍数的二元组 $(i, j)$ 的个数。

值域 $[1, W]$,$n, m, W\le 5\times 10^5$。

主要思路

前置知识:莫队二次离线

然后就只用考虑求 $S([1, i], [1, i])$ 和 $S([1, i], [l, r])$ 力。

下面将以 $n, m, W$ 同阶讨论。

那实际上就两个操作:

  1. $O(n)$ 次令当前维护区间从 $[1, i - 1]$ 变为 $[1, i]$。
  2. 求 $S([1, i], [l, r])$,个数为 $O(n)$ 且长度和为 $O(n\sqrt{n})$。

考虑维护 $c(v), b(v)$ 分别表示 $v$ 的倍数/约数的个数。
则加入一个数 $x$,新增的贡献就是 $c(x) + b(x) + 1$。

并且对于每个约数 $y$,$c(y)$ 要加 $1$;每个倍数 $z$,$b(z)$ 要加 $1$。

枚举约数 $O(\sqrt{n})$ 没啥问题。

枚举倍数 $O({n\over x})$ 时间,当 $x$ 太小就退化为 $O(n)$ 了。

于是试图阈值法,设阈值 $D=\sqrt{n}$。

然后 $x>D$ 的就直接枚举倍数,$O(\sqrt{n})$;否则不管。

但是新加一个数的贡献就出问题了。于是维护一个 $cnt(x)$ 记 $x$ 的个数,然后加入数 $x$ 的贡献就是 $1+c(x)+\sum\limits_{d|x}cnt(d)$。

那么考虑第二个操作。

长度和为 $O(n\sqrt{n})$,所以先枚举 $[l, r]$ 的每个元素,假设当前元素值为 $x$。

$x$ 的倍数个数即 $c(x)$,这个已经处理好了;
$x$ 的大于 $D$ 的约数个数即 $b(x)$,这个也已经处理好了;
现在剩下求 $x$ 的不大于 $D$ 的约数个数。

由于 $D=\sqrt{n}$,可以枚举所有不大于 $D$ 的数 $t$,求 $[l, r]$ 中 $t$ 的倍数的数量之和。容易发现这个和就是刚才没求的东西。

$[l, r]$ 中 $t$ 的倍数的数量和可以直接前缀和,维护 $pre(i, t)$ 表示前 $i$ 个数中 $t$ 的倍数的个数。预处理 $O(nD)$。

于是做完了,时间复杂度 $O(n\sqrt{n})$。

卡常

枚举一个数的因数可以 $O(n\log n)$ 空间时间预处理出来扔vector里。

如果直接维护 $pre(i, t)$,空间是 $O(nD)$,意味着 $D$ 大约只能开到 $180$。

然后俺真就直接开了 $180$,然后发现实际上跑得很慢(指勉强过)。

看了看 mrsrz 的代码,发现只开了 $D=99$,一改,果然快了不少(指快了不到 3s)。

反正卡不过 mrsrz 的手写 vector 和「阈值再分块」(其代码做 11 次连续 9 个数作为因数的信息,据说可以卡进 cache)。

参考代码

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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
#include <bits/stdc++.h>
namespace my_std {
using namespace std;
#define reg register
#define Rint register int
#define FOR(i, a, b) for (register int i = (a), ed_##i = (b); i <= ed_##i; ++i)
#define ROF(i, a, b) for (register int i = (a), ed_##i = (b); i >= ed_##i; --i)
typedef long long i64;
typedef pair<int, int> PII;
typedef vector<int> VI;
#define pb push_back
#define fr first
#define sc second
#define pq priority_queue
#define MP make_pair
#define Templ(T) template <typename T>
Templ(_Tp) inline int chkmin(_Tp &x, _Tp y) { return x > y ? x = y, 1 : 0; }
Templ(_Tp) inline int chkmax(_Tp &x, _Tp y) { return x < y ? x = y, 1 : 0; }
} // namespace my_std
using namespace my_std;

#define MMR (1 << 24)
struct INBUF{
char buf[MMR], *s, *t;
inline INBUF(){ t = (s = buf) + fread(buf, 1, MMR, stdin); }
inline INBUF& operator >>(int &x){
x = 0;
while(*s < 48) s++;
while(*s > 47) x = x * 10 + (*s++ ^ 48);
return *this;
}
}fin;
struct OUTBUF{
char buf[MMR], *s;
inline OUTBUF(){ s = buf; }
inline OUTBUF& operator <<(i64 x){
// assert(x >= 0);
static char tmp[20];
char *top = tmp;
do{ *++top = (x % 10) ^ 48, x /= 10; }while(x > 0);
while(top != tmp) *s++ = *top--;
*s++ = 10;
return *this;
}
inline ~OUTBUF(){ fwrite(buf, 1, s - buf, stdout); }
}fout;

#define N 500005
#define WM 500000
int n, m;
int a[N];
int bl[N];
int BB;

struct ASK{
int l, r, i;
inline bool operator <(const ASK &x)const{
return bl[l] != bl[x.l] ? l < x.l : (bl[l] & 1 ? r < x.r : r > x.r);
}
}ask[N];

vector<int> dvs[N];
// #define BB 846
#define VN 101
#define VB 99
int pre[N][VN];
//pre[i][v]: 前 i 个数中,是 v 倍数(包括 v)的数的个数

struct Node{
int l, r, i, v;
};
vector<Node> qL[N], qR[N];

int cnt[N];
int cc[N];//cc[v]: v 的倍数(包括 v)的个数
int bc[N];//bc[v]: v 的大于 VB 的约数(包括 v)的个数
i64 ansL[N], ansR[N];
i64 ans[N];

int main() {
fin >> n >> m;
BB = n / (sqrt(m * 2 / 3) + 1) + 1;
int mx = 0;
FOR(i, 1, n){
Rint x;
fin >> x;
a[i] = x;
bl[i] = bl[i - 1] + (i % BB == 1);
chkmax(mx, a[i]);
// FOR(j, 1, VB){
// pre[i][j] = pre[i - 1][j] + (x % j == 0);
// }
}
FOR(i, 1, mx){
for(int j = i; j <= mx; j += i){
dvs[j].pb(i);
}
}

FOR(i, 1, m) fin >> ask[i].l >> ask[i].r, ask[i].i = i;
sort(ask + 1, ask + m + 1);
ask[0].l = 1;
FOR(i, 1, m){
if(ask[i].r > ask[i - 1].r){
qR[ask[i - 1].l - 1].pb((Node){
ask[i - 1].r + 1, ask[i].r, i, -1
});
}else if(ask[i].r < ask[i - 1].r){
qR[ask[i - 1].l - 1].pb((Node){
ask[i].r + 1, ask[i - 1].r, i, 1
});
}
if(ask[i].l < ask[i - 1].l){
qL[ask[i].r + 1].pb((Node){
ask[i].l, ask[i - 1].l - 1, i, -1
});
}else if(ask[i].l > ask[i - 1].l){
qL[ask[i].r + 1].pb((Node){
ask[i - 1].l, ask[i].l - 1, i, 1
});
}
}

FOR(i, 1, n){
reg const int x = a[i];
ansL[i] = ansL[i - 1] + cc[x] + 1;
for(Rint j: dvs[x]) ansL[i] += cnt[j], ++cc[j];
if(x > VB){
for(int j = x; j <= mx; j += x) ++bc[j];
}
++cnt[x];

for(reg Node o: qR[i]){
reg i64 res = 0;
FOR(j, o.l, o.r){
res += cc[a[j]];
res += bc[a[j]];
}
// FOR(j, 1, VB){
// res += (i64)cnt[j] * (pre[o.r][j] - pre[o.l - 1][j]);
// }
o.v > 0 ? (ans[o.i] += res) : (ans[o.i] -= res);
}
}

memset(cnt, 0, sizeof(int) * N);
memset(cc, 0, sizeof(int) * N);
memset(bc, 0, sizeof(int) * N);
ROF(i, n, 1){
reg const int x = a[i];
ansR[i] = ansR[i + 1] + cc[x] + 1;
for(Rint j: dvs[x]) ansR[i] += cnt[j], ++cc[j];
if(x > VB){
for(int j = x; j <= mx; j += x) ++bc[j];
}
++cnt[x];

for(reg Node o: qL[i]){
reg i64 res = 0;
FOR(j, o.l, o.r){
res += cc[a[j]];
res += bc[a[j]];
}
// FOR(j, 1, VB){
// res += (i64)cnt[j] * (pre[o.r][j] - pre[o.l - 1][j]);
// }
o.v > 0 ? (ans[o.i] += res) : (ans[o.i] -= res);
}
}

{
const int vL = 1, vR = VB;
int *c = (int*)malloc(sizeof(int) * VB);
memset(c, 0, sizeof(int) * VB);
FOR(i, 1, n) FOR(j, vL, vR){
pre[i][j - vL] = pre[i - 1][j - vL] + (a[i] % j == 0);
}
FOR(i, 1, n){
const int x = a[i];
for(reg Node o: qL[i]){
reg i64 res = 0;
FOR(j, vL, vR){
res += (i64)(cnt[j] - c[j - vL]) * (pre[o.r][j - vL] - pre[o.l - 1][j - vL]);
}
o.v > 0 ? (ans[o.i] += res) : (ans[o.i] -= res);
}
if(vL <= x && x <= vR) ++c[x - vL];
for(reg Node o: qR[i]){
reg i64 res = 0;
FOR(j, vL, vR){
res += (i64)c[j - vL] * (pre[o.r][j - vL] - pre[o.l - 1][j - vL]);
}
o.v > 0 ? (ans[o.i] += res) : (ans[o.i] -= res);
}
}
free(c);
}

reg i64 *res = (i64*)malloc(sizeof(i64) * N);
FOR(i, 1, m){
ans[i] += ansL[ask[i].r] + ansR[ask[i].l]
- ansL[ask[i - 1].r] - ansR[ask[i - 1].l];
res[ask[i].i] = ans[i] += ans[i - 1];
}
FOR(i, 1, m) fout << res[i];
free(res);
return 0;
}

感觉这题难度主要在莫队二次离线,后面的思路其实非常自然。