- 有 (n) 个蘑菇(标号为 (0sim n-1)),分为
A
,B
两类,已知 (0) 号蘑菇属于A
类。 - 你可以进行 (Q) 次询问操作,每次挑选出若干蘑菇,以任意顺序排成一排,交互器会告诉你有多少对相邻蘑菇是不同类的。
- 要求求出有多少个蘑菇属于
A
类。 - (1le nle2 imes10^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;
}