CodeForces1617D2 Too Many Impostors (hard version)
阿新 • • 發佈:2021-12-17
題目大意
有n個玩家,n是3的倍數,有k個冒名頂替者,保證\(\frac{n}{3} < k < \frac{2n}{3}\),每次可以詢問3個玩家中冒名頂替者的數量是否大於一半,最多詢問n+6次,問冒名頂替者的數量和編號。
解題思路
3個一組的詢問,因為k的數量限制,必定至少有一組0和一組1,然後我們從兩組中各挑兩個人,進行四次詢問即可找出一個冒名頂替者,一個船員,具體看程式碼註釋。一共消耗\(\frac{n}{3} + 4\)次詢問。
把之前3個一組的詢問結果儲存下來,如果結果是1,就拿冒名頂替者問12,23兩組即可確定3人身份,結果是0類似,具體看程式碼。一共消耗\(\frac{2n}{3}\)
程式碼
const int maxn = 2e5+10; const int maxm = 2e6+10; struct TRP { int a[3]; }; TRP t0, t1; int ask(int a, int b, int c) { printf("? %d %d %d\n", a, b, c); //cout << endl; fflush(stdout); int x; scanf("%d", &x); return x; } vector<TRP> res[2]; int main() { int __; cin >> __; while(__--) { int n; scanf("%d", &n); res[0].clear(), res[1].clear(); for (int i = 1; i<=n; i+=3) { int x = ask(i, i+1, i+2); if (x) t1 = {i, i+1, i+2}; else t0 = {i, i+1, i+2}; res[x].push_back({i, i+1, i+2}); } int f0, f1, res1, res2, cnt = 0; cnt += (res1=ask(t0.a[0], t1.a[0], t1.a[1])); cnt += (res2=ask(t0.a[1], t1.a[0], t1.a[1])); /* 00 11 - 1 1 0 0 00 10 - 0 0 0 0 01 11 - 1 1 1 1 01 10 - 0 1 1 0 */ cnt += ask(t1.a[0], t0.a[0], t0.a[1]); cnt += ask(t1.a[1], t0.a[0], t0.a[1]); if (cnt==0) f0 = t0.a[0], f1 = t1.a[2]; else if (cnt==4) f0 = t0.a[2], f1 = t1.a[0]; else { if (res1+res2==2) f0 = t0.a[0], f1 = t1.a[0]; else f0 = t0.a[2], f1 = t1.a[2]; } set<int> ans; ans.insert(f0); for (auto v : res[0]) { int x = ask(f1, v.a[0], v.a[1]); int y = ask(f1, v.a[1], v.a[2]); if (x+y==0) { ans.insert(v.a[0]); ans.insert(v.a[1]); ans.insert(v.a[2]); } else if (x+y==2) { ans.insert(v.a[0]); ans.insert(v.a[2]); } else if (x) { ans.insert(v.a[1]); ans.insert(v.a[2]); } else { ans.insert(v.a[0]); ans.insert(v.a[1]); } } for (auto v : res[1]) { int x = ask(f0, v.a[0], v.a[1]); int y = ask(f0, v.a[1], v.a[2]); if (x+y==2) continue; if (x==1) ans.insert(v.a[2]); else if (y==1) ans.insert(v.a[0]); else ans.insert(v.a[1]); } printf("! %d", (int)ans.size()); for (auto v : ans) printf(" %d", v); putchar('\n'); fflush(stdout); } return 0; }