1. 程式人生 > 其它 >【題解】P5355 [Ynoi2017] 由乃的玉米田

【題解】P5355 [Ynoi2017] 由乃的玉米田

題意

P4688 [Ynoi2016] 掉進兔子洞

給定一個長為 \(n\) 的序列和 \(m\) 個操作,每次操作詢問區間 \([l, r]\) 內是否可以選出兩個數 \(a_x, a_y\),使得:

  1. \(a_x - a_y = x\)

  2. \(a_x + a_y = x\)

  3. \(a_x \cdot a_y = x\)

  4. \(\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 + 根號分治。

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