【题解】P5355 [Ynoi2017] 由乃的玉米田
题意
给定一个长为 \(n\) 的序列和 \(m\) 个操作,每次操作询问区间 \([l, r]\) 内是否可以选出两个数 \(a_x, a_y\),使得:
-
\(a_x - a_y = x\)
-
\(a_x + a_y = x\)
-
\(a_x \cdot a_y = x\)
-
\(\min(a_x, a_y) \mid \max(a_x, a_y)\) 且 \(\frac{\max(a_x, a_y)}{\min(a_x, a_y)} = x\)
\(n, m \leq 10^5, 0 \leq x \leq 10^5, 1 \leq a_i \leq 10^5\)
思路
莫队 + bitset
+ 根号分治。
P3674 小清新人渣的本愿 的强化版。
不用修改,不强制在线,考虑 莫队 处理。
不妨令 \(N\) 为某一极大值,同时维护两个 bitset
\(s1, s2\),其中 \(s1\) 表示值 \(x\) 是否在当前区间内出现过,\(s2\) 表示值 \(N - x\) 是否在当前区间内出现过。
对于操作 \(1\),如果 \([l, r]\) 内存在 \(a - b = x\),因为 \(b\) 可以表示成 \(a - x\),所以 \([l, r]\) 内也一定存在 \(a, a - x\),则问题转化成:是否可以在 \([l, r]\) 内选出 \(a\) 使得 \([l, r]\) 内同时存在 \(a, a - x\),可以用 s1[a] == s1[a - x] == true
判断。不难发现上面的代码等价于 (s1 & (s1 << x)).any()
。
对于操作 \(2\),回顾 \(s2\) 的定义,发现 (s2 >> (N - x))[i]
实际上维护的是 \([l, r]\) 内是否存在 \((N - i) - (N - x) = x - i\)。故而我们可以用 (s1 & (s2 >> (N - x))).any()
判断 \([l, r]\) 内是否存在 \(a + b = x\)。由于 bitset
不能维护负数下标,\(N\) 应该取到 \(10^5\) 及以上。
对于操作 \(3\),因为 \(0 \leq x \leq 10^5\),所以可以暴力枚举 \(x\) 的因数并用 \(s1\) 判断。
对于操作 \(4\),发现难以直接用莫队维护。令 \([l, r]\) 中的最大值为 \(k\),因为当 \(x \geq \sqrt{k}\) 时,暴力枚举商为 \(x\) 的两个数复杂度是 \(\mathcal{O}(\sqrt{n})\),所以可以考虑 根号分治。
当 \(x \geq \sqrt{k}\) 时,暴力枚举商为 \(x\) 的两个数即可。
当 \(x < \sqrt{k}\) 时,考虑 \(\mathcal{O}(n\sqrt{n})\) 预处理答案。令 \(a_i\) 的最大值为 \(K\),不妨对于 \(1 \leq x < \sqrt{K}\) 预处理 \(p_i\),\(p_i = l\) 表示 \([l, i]\) 内存在 \(\frac{a}{b} = x\) 且 \(i - l\) 最小。对于询问 \([l, r]\) 内是否存在 \(\frac{a}{b} = x\),显然若 \(l \leq p_r\) 则存在,反之则不存在。
莫队部分时间复杂度 \(\mathcal{O}(n \sqrt{n})\),根号分治时间复杂度 \(\mathcal{O}(n \sqrt{n})\),因此总时间复杂度 \(\mathcal{O}(n \sqrt{n})\)。
代码
#include <cstdio>
#include <cmath>
#include <cstring>
#include <vector>
#include <bitset>
#include <algorithm>
using namespace std;
#define rint register int
inline int read(){
rint x=0,c=getchar();
for(;c<48||c>57;c=getchar());
for(;c>47&&c<58;c=getchar())x=x*10+(c^48);
return x;
}
const int maxn = 1e5 + 5;
int n, m;
int maxv, len;
int lst[maxn], pre[maxn];
int a[maxn], bel[maxn], cnt[maxn];
bool ans[maxn];
bitset<maxn> s1, s2;
struct node {
int l, r, x, opt, id;
node(): l(), r(), x(), opt(), id() {}
node(int _l, int _r, int _x, int _opt, int _id): l(_l), r(_r), x(_x), opt(_opt), id(_id) {}
bool operator < (const node& rhs) const {
if (bel[l] ^ bel[rhs.l]) {
return bel[l] < bel[rhs.l];
}
return (bel[l] & 1 ? r < rhs.r : r > rhs.r);
}
} q1[maxn];
vector<node> q2[maxn];
inline void add(rint x) {
cnt[a[x]]++;
if (cnt[a[x]] == 1) {
s1[a[x]] = true;
s2[maxv - a[x]] = true;
}
}
inline void del(rint x) {
cnt[a[x]]--;
if (!cnt[a[x]]) {
s1[a[x]] = false;
s2[maxv - a[x]] = false;
}
}
int main() {
rint opt, l, r, x, tmp;
rint left = 0, right = 0;
n = read(), m = read();
rint block = sqrt(n);
for (rint i = 1; i <= n; i++) {
a[i] = read();
maxv = max(maxv, a[i] + 1);
bel[i] = (i - 1) / block + 1;
}
block = sqrt(maxv);
for (rint i = 1; i <= m; i++) {
opt = read(), l = read(), r = read(), x = read();
if ((opt == 4) && (x < block)) {
q2[x].push_back(node(l, r, x, opt, i));
} else {
len++;
q1[len] = node(l, r, x, opt, i);
}
}
sort(q1 + 1, q1 + len + 1);
for (rint i = 1; i <= len; i++) {
while (left > q1[i].l) {
add(--left);
}
while (right < q1[i].r) {
add(++right);
}
while (left < q1[i].l) {
del(left++);
}
while (right > q1[i].r) {
del(right--);
}
if (q1[i].opt == 1) {
ans[q1[i].id] = (s1 & (s1 << q1[i].x)).any();
} else if (q1[i].opt == 2) {
ans[q1[i].id] = (s1 & (s2 >> (maxv - q1[i].x))).any();
} else if (q1[i].opt == 3) {
for (rint j = 1; j * j <= q1[i].x; j++) {
if (q1[i].x % j == 0) {
if (s1[j] && s1[q1[i].x / j]) {
ans[q1[i].id] = true;
break;
}
}
}
} else {
for (rint j = 1; q1[i].x * j <= maxv; j++) {
if (s1[j] && s1[q1[i].x * j]) {
ans[q1[i].id] = true;
break;
}
}
}
}
for (rint i = 1; i < block; i++) {
if (!q2[i].size()) {
continue;
}
memset(lst, 0, (maxv + 1) * sizeof(int));
memset(pre, 0, (n + 1) * sizeof(int));
for (rint j = 1; j <= n; j++) {
pre[j] = pre[j - 1];
lst[a[j]] = j;
tmp = a[j] * i;
if (tmp <= maxv) {
pre[j] = max(pre[j], lst[tmp]);
}
tmp = a[j] / i;
if (a[j] % i == 0) {
pre[j] = max(pre[j], lst[tmp]);
}
}
for (rint j = 0; j < q2[i].size(); j++) {
ans[q2[i][j].id] = (q2[i][j].l <= pre[q2[i][j].r]);
}
}
for (rint i = 1; i <= m; i++) {
putchar('y'), putchar('u');
if (ans[i]) {
putchar('n'), putchar('o');
} else {
putchar('m'), putchar('i');
}
putchar('\n');
}
return 0;
}