【題解】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]\)
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;
}