P3674 小清新人渣的本願(莫隊+bitset)
阿新 • • 發佈:2021-11-18
目錄
Description
給你一個序列 ,長度為 ,有 次操作,每次詢問一個區間是否可以選出兩個數它們的差為 ,或者詢問一個區間是否可以選出兩個數它們的和為 ,或者詢問一個區間是否可以選出兩個數它們的乘積為 ,這三個操作分別為操作 。
選出的這兩個數可以是同一個位置的數。
State
\(n, m, max(x, a[i])<=10^5\)
\(a[i]>=0, x >= 2\)
Input
5 5
1 1 2 3 4
2 1 1 2
1 1 2 2
3 1 1 1
3 5 5 16
1 2 3 4
Output
hana
bi
hana
hana
bi
Solution
看到題目的第一眼感覺用一個數組覺可以解決,但是題目設計的比較妙
如果上一次是 \(1\) 操作,當前為 \(2\) 操作,區間都相同,那麼該怎麼辦呢
這提示我們需要用一種複雜度極低的查詢方式,可以利用 \(bitset\) 的與操作
- \(bitset & (bitset << x)\)
- \(a+b=x\) 其中 \(a,b\) 是在 \(bitset\) 中出現過的數,將起轉化為\(N-a-b=N-x\),也就是 \((N-b)-a=(N-x)\),這樣又變成了 \(1\) 的形式
- 這個就比較套路了,列舉 \(x\) 的因子就可以了
Code
const int N = 1e5 + 5; int n, m; int a[N]; struct Query { int l, r; int bel; int id, opt, x; bool operator<(Query o){ return bel == o.bel ? r < o.r: l < o.l; } void read(){ sd(opt), sdd(l, r), sd(x); } }q[N]; int block, vis[N]; bool ans[N]; bitset<N> now, rev; void mo() { block = 3 * sqrt(n); for(int i = 1; i <= m; i ++){ q[i].id = i; q[i].bel = q[i].l / block + 1; } sort(q + 1, q + 1 + m); } void add(int pos) { int x = a[pos]; vis[x] ++; now[x] = 1; rev[N - x] = 1; } void del(int pos) { int x = a[pos]; vis[x] --; if(vis[x]) return ; now[x] = 0; rev[N - x] = 0; } signed main() { while(~ sdd(n, m)){ rep(i, 1, n) sd(a[i]); rep(i, 1, m) q[i].read(); mo(); int l = 1, r = 0; now = 0; rev = 0; for(int i = 1; i <= m; i ++){ while(l > q[i].l) add(-- l); while(r < q[i].r) add(++ r); while(l < q[i].l) del(l ++); while(r > q[i].r) del(r --); // dbg(now[1]); if(q[i].opt == 1){ ans[q[i].id] = ((now & (now << q[i].x))).any(); } else if(q[i].opt == 2){ ans[q[i].id] = ((rev & (now << N - q[i].x)).any()); } else{ for(int k = 1; k * k <= q[i].x; k ++){ if(q[i].x % k == 0 && now[k] && now[q[i].x / k]){ ans[q[i].id] = 1; break; } } } } rep(i, 1, m) puts(ans[i]? "hana": "bi"); } return 0; }