題解 P5355 【[Ynoi2017]由乃的玉米田】
題意簡述
見題面
題解
前三個操作就是小清新人渣的本願。
這裡簡單講解一下。
記錄兩個 bitset cla
和 inv
。
我們考慮莫隊。
cla[x]==1
表示 \(x\) 這個數出現過。
inv[x]==1
表示 \(100000-x\) 這個數出現過。
這兩個 bitset 的維護很簡單,就是在莫隊的加減貢獻操作裡改就行了。
對於第一個減法的判斷,我們的答案就是 ((cla<<x)&cla)
是否為 0。
如果為 0 的話表示應該輸出有解。
正確性很好得到。
比如我們的詢問是是否存在 \(a,b\) 使得 \(a-b=x\)。
那麼我們只需要存在 \(a\) 以及 \(a-x\)
第二個加法的判斷也差不多,看作是加一個負數即可,判斷是 ((cla<<(100000-x)&inv))
。
第三個乘法的判斷直接暴力列舉因子 \(i\),判斷 \(i,\frac{x}{i}\) 是否同時存在即可。(\(i\mid x\))。
由於值域和 \(n,m\) 同階,所以我們的複雜度是對的。
對於第四個操作我們直接從乘法賀過來。
列舉一個 \(i\),從 1 開始,終止條件為 \(i\times x\le100000\)。
其中 \(x\) 為當前詢問給出的商。
然後直接判斷是否同時存在 \(i\) 和 \(i\times x\) 即可。
在 \(x\ge\sqrt{n}\)
那麼 \(x<\sqrt{n}\) 的時候我們就換一種方法吧。
我們列舉一個 \(x\in[1,\sqrt{100000}]\)。
然後維護兩個陣列 pre
和 mxp
。
在 \(x\) 的列舉裡面我們再列舉一個 \(i\in[1,n]\)。
然後 pre[i]
表示 \(a_{i}\) 的上一次出現位置。
mxp[i]
掃描到 \(i\) 的時候出現了滿足 \(a\div b=x\) 的最右位置。
維護的具體方法看註釋吧。
這道題就完了。
#include <cstdio> #include <cstring> #include <algorithm> #include <bitset> #include <queue> using namespace std; const int Maxn = 1e5 + 5, Maxs = 400 + 5, Maxv = 310; int n, m, each, cube, isa[Maxn], cnt[Maxn], ans[Maxn], pre[Maxn], mxp[Maxn], bel[Maxn], lps[Maxs], rps[Maxs]; struct Query { int t, l, r, x, id; Query() {} Query(int t1, int t2, int t3, int t4, int t5) { t = t1; l = t2; r = t3; x = t4; id = t5; } }; struct Special { int l, r, id; Special() {} Special(int t1, int t2, int t3) { l = t1; r = t2; id = t3; } }; vector < Query > Mq; // Mo's Algorithm | Query vector < Special > Vq[Maxn]; // Violence | Query bitset < Maxn > cla, inv; // classic | inverse bool cmp(const Query &one, const Query &ano) { if (bel[one.l] != bel[ano.l]) return one.l < ano.l; else if (bel[one.l] & 1) return one.r < ano.r; else return one.r > ano.r; } void Plus_Cont(int x) { x = isa[x]; if (cnt[x] == 0) { cla[x] = 1; inv[100000 - x] = 1; } ++cnt[x]; } void Mins_Cont(int x) { x = isa[x]; --cnt[x]; if (cnt[x] == 0) { cla[x] = 0; inv[100000 - x] = 0; } } void Pare_v1() { int l = 1, r = 0; for (auto it : Mq) { while (l > it.l) Plus_Cont(--l); while (l < it.l) Mins_Cont(l++); while (r > it.r) Mins_Cont(r--); while (r < it.r) Plus_Cont(++r); if (it.t == 1) ans[it.id] = ((cla << it.x) & cla).any(); else if (it.t == 2) ans[it.id] = ((cla << (100000 - it.x)) & inv).any(); else if (it.t == 3) { bool flag = 0; for (int i = 1; i * i <= it.x; ++i) { if (it.x % i == 0 && cla.test(i) && cla.test(it.x / i)) { ans[it.id] = 1; flag = 1; break; } } if (flag == 0) ans[it.id] = 0; } else { bool flag = 0; for (int i = 1; i * it.x <= 100000; ++i) { if (cla.test(i) && cla.test(i * it.x)) { ans[it.id] = 1; flag = 1; break; } } if (flag == 0) ans[it.id] = 0; } } } void Pare_v2() { for (int x = 1; x <= Maxv; ++x) { if (Vq[x].empty()) continue; int now = 0; for (int i = 1; i <= n; ++i) { int y = isa[i]; pre[y] = i; if (x * y <= 100000) now = max(now, pre[x * y]); if (y % x == 0) now = max(now, pre[y / x]); mxp[i] = now; } for (auto it : Vq[x]) { if (it.l <= mxp[it.r]) ans[it.id] = 1; else ans[it.id] = 0; } memset(pre, 0, sizeof pre); memset(mxp, 0, sizeof mxp); } } char ANS[2][10] = { "yumi", "yuno" }; signed main() { scanf("%d %d", &n, &m); each = 320; cube = (n - 1) / each + 1; for (int i = 1; i <= n; ++i) scanf("%d", &isa[i]); for (int i = 1; i <= cube; ++i) { lps[i] = rps[i - 1] + 1; rps[i] = rps[i - 1] + each; if (i == cube) rps[i] = n; for (int j = lps[i]; j <= rps[i]; ++j) bel[j] = i; } for (int i = 1, t, l, r, x; i <= m; ++i) { scanf("%d %d %d %d", &t, &l, &r, &x); if (t == 4 && x <= Maxv) Vq[x].emplace_back(Special(l, r, i)); else Mq.emplace_back(Query(t, l, r, x, i)); } sort(Mq.begin(), Mq.end(), cmp); Pare_v1(), Pare_v2(); for (int i = 1; i <= m; ++i) puts(ANS[ans[i]]); return 0; }