把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【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\),减去已知的 AB 之间的一点固定贡献之后,就能一波推出 \(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;
}
posted @ 2021-09-02 18:10  TheLostWeak  阅读(170)  评论(0编辑  收藏  举报