题意简述
有一个含有 n n n 个数字的数组 a a a (n ≤ 1000 n\le1000 n ≤ 1000 ),有 k k k 个不重合的集合 S 1 , S 2 , ⋯ , S k S_1,S_2,\cdots,S_k S 1 , S 2 , ⋯ , S k ,集合元素为 1 1 1 到 n n n 的整数,由此生成一个长度为 k k k 的密码 p p p ,定义 p i = max j ∉ S i a j p_i=\displaystyle\max_{j\notin S_i}a_j p i = j ∈ / S i max a j ,允许进行至多 12 12 12 次询问猜出密码,每次询问一个集合 S 0 S_0 S 0 ,返回 max i ∈ S 0 a i \displaystyle\max_{i\in S_0}a_i i ∈ S 0 max a i 。
思路分析
注意到 S i S_i S i 不交,因此除了包含的所有最大元素的集合外(这样的集合不一定存在),其他所有位置的密码都是最大值,因此这道题包含两个步骤:
找到最大值;
找到含最大值一组的答案。
对于第一个步骤,只要询问 1 1 1 到 n n n 的答案即可,对于第二个步骤,可以再分为两步:
找到最大值所在的集合;
找到这个集合的答案。
对于第一个步骤,结合次数限制,容易想到二分查找。具体的,先假设最大值只有一个(之后解释正确性),每一次询问当前区间的一半,检查结果是否等于最大值,确定最大值在哪一半,缩小范围直到找到最大值,再找到最大值所在的集合编号。
对于第二个步骤,按照题目描述查询除去最大值所在集合即可。
正确性分析
询问次数不超过 12 12 12
分析所有的询问,步骤 1 1 1 花费 1 1 1 次查询,步骤 2.1 2.1 2.1 花费至多 log 2 1000 = 10 \log_21000=10 log 2 1000 = 10 次查询,步骤 2.2 2.2 2.2 花费 1 1 1 次查询,总共至多 12 12 12 次查询。
假设最大值只有一个不会影响结果
考虑有多个最大值的情况:若所有最大值都在同一集合,则最后找到的最大集合无误;若最大值在不同集合,则所有的答案都是最大值,无论找到哪个集合都不影响。
易错提醒
注意处理最大值为不属于任何集合的情况。
一定要清空(也是为了应对不属于任何集合的元素),否则 ILE on test 7 \color{red}\text{ILE on test 7} ILE on test 7 。
代码实现
不要进行没有意义的抄袭。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 int num[2005 ],idx[2005 ];int n,k;void solve () { cin>>n>>k; for (int i=1 ;i<=n;i++){ num[i]=idx[i]=0 ; } (num,idx)=INPUT (); int l=1 ,r=n; int mx,pos; mx=ASK (1 ~n); while (l<=r){ int mid=(l+r)/2 ; int ans; ans=ASK (l~mid); if (ans==mx){ r=mid-1 ; pos=mid; } else { l=mid+1 ; } } cout<<"? " <<n-num[idx[pos]]<<" " ; for (int i=1 ;i<=n;i++){ if (idx[i]!=idx[pos]||idx[pos]==0 ){ cout<<i<<" " ; } } cout<<endl; int ans; cin>>ans; cout<<"! " ; for (int i=1 ;i<=k;i++){ if (idx[pos]==i){ cout<<ans<<" " ; } else { cout<<mx<<" " ; } } cout<<endl; string result; cin>>result; }