题解:P6837 [IOI 2020] 数蘑菇
考虑如何通过查询来尽可能多地得到蘑菇地种类信息。
算法一
由于开始已知了一个 \(A\) 型蘑菇 \(0\)。查询 \(\{0,x\}\) 就可以得知 \(x\) 的类型。
这样需要 \(n-1\) 次,期望得分 \(10 pts\)。
算法二
假设我们此时已知了 \(x\) 个 \(A\) 型蘑菇,\(y\) 个 \(B\) 型蘑菇,不妨设 \(x>y\)。
如果我们询问 \(\{A_1 p_1 A_2 p_2 \dots A_x p_x\}\),记结果为 \(res\),我们就可以得到 \(p_{1\dots x}\) 中有 \(\lfloor\frac{res}{2}\rfloor\) 个 \(B\) 型蘑菇,其余为 \(A\) 型蘑菇,一次查询直接解决了 \(x\) 个蘑菇。
同时,根据 \(res\) 的奇偶性,可以判断 \(p_x\) 的类型。那么,每次把新的 \(p_x\) 也加入 \(x\) 或 \(y\) 中,使得增长速度加快。在交互库自适应的条件下,需要 \(280\) 次左右。
期望得分 \(80pts\)。
算法三
算法三是对算法一的优化。
考虑查询 \(\{A_1 p A_2 q\}\)。除了通过奇偶性判断 \(q\) 以外,\(q\) 确定了也可以使得 \(p\) 确定。达到了一次确定两个的效果。
这样就需要 \(\frac{n-1}{2}\) 次,期望得分 \(25pts\)。
算法四
算法四是对算法一的进一步优化。
考虑通过两次查询来确定五个蘑菇 \(p_{1\sim 5}\)。
先查询 \(\{A_1p_1A_2p_2A_3p_3\}\),直接得到了 \(p_3\),同时如果 \(p_1\) 和 \(p_2\) 种类相同的话,也可以得到。
先假设 \(p_1,p_2\) 种类不同。
考虑查询 \(\{A_1p_1A_2B_1p_2B_2p_4B_3p_5\}\)。
首先通过奇偶性得到 \(p_5\),然后发现 \(p_1,p_2\) 的贡献为 \(0/4\),\(p_4\) 的贡献为 \(0/2\),可以直接分离出每个部分的贡献,分别分析出种类。
这样就需要 \(\frac{2(n-1)}{5}\) 次,期望得分还是 \(25pts\)。
算法五
算法三和四的意义在于加速算法二的增长速度。算法二每两次只能增长 \(1\) 的速度,然而套用算法三和四就可以更快。
考虑使用算法三。
根据贪心可以得到,在一开始使用完算法三是较优的。即考虑先在 \(\max(x,y)\le m\) 时使用算法三。然后再使用算法二,通过调整参数 \(m\) 发现,最优在 \(m=80\sim 90\) 时,得分 \(92pts\)。
考虑使用算法五。
在 \(m=100\) 时,期望得分 \(100pts\)。
实现细节:
-
在 \(\max(x,y)<2\) 时,只能用算法一,所以有必要实现算法一。
-
在 \(\min(x,y)<3\) 是,不能用算法四,所以有必要实现算法二。在此情况下如果 \(\min(x,y)\) 一直达不到 \(3\),也就是一直发现数量多的一种,那么用算法二 \(\max(x,y)\) 会增长的很快,反而更优。
-
当使用算法二时,如果 \(\max(x,y)\) 已经大于剩余的未知蘑菇数量,不要改用算法一三四来做剩下的,使用算法二就可以一次解决。
#include <bits/stdc++.h>
using namespace std;
// inline int read(){
// int d=0,f=0;char ch=getchar();
// while (!isdigit(ch)) f|=(ch=='-'),ch=getchar();
// while (isdigit(ch)) d=d*10+ch-'0',ch=getchar();
// return f?-d:d;
// }
const int N=20005;
int n,sum,R,A[N],B[N],cA,cB,col[N],cnt,tot;
int use_machine(vector<int> x);
// {
// int lst=-1,res=0;cnt++;
// for (int i:x){
// if (lst!=-1&&col[i]!=col[lst]) res++;
// lst=i;tot++;
// }
// return res;
// }
inline void solve1(){//1 换 1
int res=use_machine({0,++R});
if (res) B[++cB]=R;else A[++cA]=R;
}
inline void solve2(){//1 换 2
if (cA>=2){
int res=use_machine({A[1],R+1,A[2],R+2});
if (res%2==0) A[++cA]=R+2;else B[++cB]=R+2,res--;
if (!res) A[++cA]=R+1;else B[++cB]=R+1;
}
else {
int res=use_machine({B[1],R+1,B[2],R+2});
if (res%2==0) B[++cB]=R+2;else A[++cA]=R+2,res--;
if (!res) B[++cB]=R+1;else A[++cA]=R+1;
}
R+=2;
}
inline void solve3(){//2 换 5
int r1=use_machine({A[1],R+1,A[2],R+2,A[3],R+3});
if (r1%2==0) A[++cA]=R+3;else B[++cB]=R+3,r1--;
if (r1==0){A[++cA]=R+1,A[++cA]=R+2,R+=3;return ;}//见好就收
if (r1==4){B[++cB]=R+1,B[++cB]=R+2,R+=3;return ;}
int r2=use_machine({A[1],R+1,A[2],B[1],R+2,B[2],R+4,B[3],R+5})-1;//注意减一
if (r2%2==0) B[++cB]=R+5;else A[++cA]=R+5;
if (r2&2) A[++cA]=R+4;else B[++cB]=R+4;
if (r2&4) B[++cB]=R+1,A[++cA]=R+2;
else A[++cA]=R+1,B[++cB]=R+2;
R+=5;
}
inline void solve4(){//1 换 n
vector <int> V;
if (cA>cB) {
for (int i=1;i<=cA&&R<n-1;i++) V.push_back(A[i]),V.push_back(++R);
int res=use_machine(V);sum+=V.size()/2-1-res/2;
if (res&1) B[++cB]=R;else A[++cA]=R;
}
else {
for (int i=1;i<=cB&&R<n-1;i++) V.push_back(B[i]),V.push_back(++R);
int res=use_machine(V);sum+=res/2;
if (res&1) A[++cA]=R;else B[++cB]=R;
}
}
int count_mushrooms(int _n){
n=_n,A[++cA]=0;
while (max(cA,cB)<2&&R<n-1) solve1();//解锁1换2
while (max(cA,cB)<=100){//解锁1换n
if (R+5>=n) {
while (R!=n-1) solve4();
return sum+cA;
}
if (min(cA,cB)<3) solve2();
else solve3();
}
while (R!=n-1) solve4();
return sum+cA;
}
// int main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
// int _n=read();
// for (int i=0;i<_n;i++) col[i]=read();
// int ans=count_mushrooms(_n);
// printf("%d\n",ans);
// if (cnt>226||tot>100000)
// printf("%d %d\n",cnt,tot);
// // printf("ans:%d sum:%d cA:%d R:%d\n",ans,sum,cA,R);
// }

浙公网安备 33010602011771号