【題解】「JOISC 2019 Day4」礦物
阿新 • • 發佈:2022-05-27
互動題,給定 \(N\) 種顏色,每種顏色恰好 \(2\) 個球,每次可以向集合中插入/刪除一個球,然後得到集合中有多少種顏色。你需要在 \(10^6\) 次操作內將球兩兩配對 \(N\le 4.3\times 10^4\)。
首先不難想到生日悖論,每次隨機向集合中加入一個球,當集合中出現相同的球時就配對,然後清空集合。這樣做期望次數是 \(\mathcal{O}(N\sqrt N)\),實際得分 \(6\) 分。
這個資料範圍顯然是留給 \(\log\) 做法的,我們優先考慮分治。現在我們 solve(S)
表示將集合 \(S\) 中的球配對。假設一共有 \(n\) 對,取 \(m = n / 2\)
想辦法優化常數,如果我們在呼叫 solve(S)
的時候,已經將每對球中恰好一個球加入集合中,那麼我們掃一遍可以同時分出兩組 \(m\) 對球,並且每對球都是恰好一個在集合中。這樣做每次 solve
的操作次數都是嚴格小於 \(|S|\) 次,但由於毒瘤出題人,仍只有 \(40\) 分,但是操作次數已經由 \(2\times 10^6\) 優化到 \(1.3\times 10^6\)。
我們發現操作多的原因在於掃的時候,如果一個球不能配對,就需要再操作一次把它放回集合。我們觀察一下發現,我們不需要每對球恰好一個在集合中,我們只需要將 \(S\)
solve
的操作次數都是嚴格小於 \(\frac{3}{4}|S|\),可以得到 \(85\) 分。
由於出題人喪心病狂的卡常,最後一個點多了大概一萬次操作我們注意到每次 solve
有 \(\frac{3}{4}\) 的常數,那麼我們每次在中點分治並不是最優的,將分治點偏移一點,調參就過了。
#define S 86005 mt19937 rd(time(0)); int v[S], lst = 0, idx, id[S]; int Query(int); void Answer(int,int); int ask(int x){v[x] ^= 1; return Query(x);} void solve(vector<int>c){ int n = si(c); if(n == 2){Answer(c[0], c[1]); return ;} vector<int>p, q, l, r; go(x, c)if(id[x])p.pb(x); else q.pb(x); int m = max(1, (int)(n * 0.185)), s = 0; rep(i, 0, m - 1)lst = ask(q[i]), r.pb(q[i]); rep(i, m, si(q) - 1)l.pb(q[i]); int op = !v[q[0]]; go(x, p){ if(s == m)l.pb(x); else{ int w = ask(x); if((w != lst) ^ op)l.pb(x); else r.pb(x), s++; lst = w; } } solve(l), solve(r); } void Solve(int n){ n <<= 1; vector<int>a; rp(i, n)a.pb(i); shuffle(a.begin(), a.end(), rd); rp(i, n){ int x = a[i - 1]; int w = ask(x); if(w == lst)id[x] = 1; lst = w; } solve(a); }