【UOJ568】【IOI2020】Mushrooms(交互)
- 有 \(n\) 个蘑菇(标号为 \(0\sim n-1\)),分为
A
,B
两类,已知 \(0\) 号蘑菇属于A
类。 - 你可以进行 \(Q\) 次询问操作,每次挑选出若干蘑菇,以任意顺序排成一排,交互器会告诉你有多少对相邻蘑菇是不同类的。
- 要求求出有多少个蘑菇属于
A
类。 - \(1\le n\le2\times10^4\),\(Q=226\)
大批的询问方式
容易发现根据 A
+\(i_1\)+A
+\(i_2\)+\(\cdots\)+A
+\(i_{k}\)(其中的 A
可以全部替换为 B
)的形式,可以得知这 \(k\) 个蘑菇中共有多少个 A
类蘑菇。同时,由于只有标号为 \(i_k\) 的蘑菇的贡献是 \(0/1\),其余都是 \(0/2\),我们还可以确定出 \(i_k\) 这一个蘑菇的种类。
当已知大批蘑菇的种类时,这应该算是一种比较优的询问方式了。但如果全部用这样搞,由于起步过慢,并不能在限定次数内求出答案。
所以,我们需要在一开始先快速求出一些蘑菇的种类,然后再按照上面的方式去做。
初始的询问方式
如果每种最多只知道一个蘑菇,那么也只能用 A
+\(x\) 询问,来求出 \(x\) 的种类。
当某一种有两个蘑菇了,容易想到每次询问 A
+\(x\)+A
+\(y\),一次询问可以求出两个蘑菇的种类。然而这样并不优秀。
考虑我们每次直接询问 A
+\(x\)+A
+\(y\)+A
+\(z\),那么 \(z\) 的种类可以直接确定,如果 \(x\) 和 \(y\) 的种类相同也可以直接确定它们。
关键问题就在于 \(x,y\) 种类不同,此时我们可以再带着它们询问一次 A
+\(x\)+A
+\(u\)+A
+B
+\(y\)+B
+\(v\)。(如果 B
的个数此时还不到两个,我们可以用 A
+\(x\) 确定 \(x\) 的种类,也就确定了 \(y\) 的种类。因为 \(x,y\) 种类相反,最多出现两次这样的情况。) \(x\) 和 \(y\) 种类相反所以贡献之和是 \(0/4\),\(u\) 的贡献是 \(0/2\),\(v\) 的贡献是 \(0/1\),减去已知的 A
和 B
之间的一点固定贡献之后,就能一波推出 \(x,y,u,v\) 的种类。
综上,最坏的情况是通过两次询问求出 \(5\) 个蘑菇的种类,平均一次询问可以求出 \(2.5\) 个。
然后稍微调调阈值就过了。
代码:\(O(n)\)
#include "mushrooms.h"
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Tq template<typename... Ar>
#define Ts template<typename Ty,typename... Ar>
#define Rg register
#define RI Rg int
#define Cn const
#define CI Cn int&
#define I inline
#define W while
#define pb push_back
#define eb pop_back
using namespace std;
vector<int> S,P[2];
Tp I int Q(Ty x) {return S.pb(x),use_machine(S);}
Ts I int Q(Ty x,Ar... y) {return S.pb(x),Q(y...);}
Tq I int G(Ar... y) {return S.clear(),Q(y...);}
int count_mushrooms(int n)
{
RI c=1;P[0].pb(0);W(c^n&&(int)max(P[0].size(),P[1].size())<2) P[G(0,c)].pb(c),++c;//每种最多只知道一个蘑菇:A+x
RI o,op=P[1].size()==2;W(c+1<n&&(int)max(P[0].size(),P[1].size())<3) o=G(P[op][0],c,P[op][1],c+1),P[(o>>1)^op].pb(c++),P[(o&1)^op].pb(c++);//每种最多只有两个蘑菇:A+x+A+y
RI i;for(i=1;c<=199&&c+2<n;++i,c+=3)//设个阈值,先快速求出一些蘑菇的种类
{
op=P[0].size()<P[1].size(),o=G(P[op][0],c,P[op][1],c+1,P[op][2],c+2),P[(o&1)^op].pb(c+2);//A+x+A+y+A+z,确定z
if(!(o&2)) {P[(o>>2)^op].pb(c),P[(o>>2)^op].pb(c+1);continue;}//如果x,y相同可以直接确定
if(P[op^1].size()<2||c+4>=n) {P[o=G(0,c)].pb(c),P[o^1].pb(c+1);continue;}//如果另一种蘑菇少于2个直接A+x
o=G(P[op][0],c,P[op][1],c+3,P[op][2],P[op^1][0],c+1,P[op^1][1],c+4)-1,//A+x+A+u+A+B+y+B+v
P[(o&1)^op^1].pb(c+4),P[(o>>1&1)^op].pb(c+3),P[(o>>2)^op].pb(c),P[(o>>2)^op^1].pb(c+1),c+=2;//一波推出x,y,u,v
}
RI k,t=P[0].size();W(c^n)//大批的询问方式
{
for(k=P[op=P[0].size()<P[1].size()].size(),S.clear(),i=0;i^k&&c^n;++i) S.pb(P[op][i]),S.pb(c++);//A+i1+A+i2+...+A+ik
o=use_machine(S),op?t+=o+1>>1:t+=i-(o+1>>1),P[(o&1)^op].pb(c-1);//更新答案,同时求出ik的种类
}return t;
}