题解 P7045 【「MCOI-03」金牌】
Owen_codeisking
2020-11-03 21:33:35
交互好题!前排膜拜出题人。
首先有个经典算法:若序列中存在一个数出现次数 $>\lfloor \frac n2\rfloor$,那么执行下列算法即可找到这个数:
- $i=1,count=1,value=a_1$
- $i\leftarrow i+1$。若 $value=a_i$,令 $count=count+1$。否则,令 $count=count-1$。
- 若 $count=0$,令 $count=1,value=a_i$。
- 重复执行操作 $2,3$ 直到 $i=N$。
(正确性容易证明?)
对于一般序列,可以发现执行这个算法后,$value$ 是唯一有可能出现次数 $>\lfloor \frac n2\rfloor$ 的数。
那么这个经典算法又和这题有什么关系呢?
若存在一个数出现次数 $>\lfloor \frac n2\rfloor$,那么一定无解。
那么,可以先用 $n-1$ 次询问找出 $value$,再用 $n-1$ 次询问得到 $value$ 的出现次数。
什么?你说了这么多只能判个无解?就这就这?
~~怎么可能就这~~
我们发现,当这个算法 $count=0$ 时,实际能得到两个序列 $A,B$,满足 $A$ 所有元素相同,$B$ 所有元素与 $A$ 不同,且 $|A|=|B|$。
若原来的 $value=V$,更新过后是 $V'$,那么我们可以构造序列 $C'=C+\{V,B_1,V,B_2,...,V,V'\}$。
重复上述过程,我们最后会得到序列 $A,B,C$,同时 $|A|>|B|$。那么再次执行类似这个过程,会得到序列 $A$ 和空序列 $B$。那么我们要将序列 $A$ 中的元素插入序列 $C$。
因为构造出来序列 $C$ 相邻的数不同,所以这个序列有一个很强的性质:保证 $A$ 能插入的位置数量最多。
(正确性容易证明?)
那么,我们先用 $n-1$ 次询问找出 $value$,再用 $n-1$ 次询问序列 $C$ 中每个元素是否等于 $value$,即可在 $Q=2n-2$ 次询问内解决问题。
~~感觉用 vector 模拟代码有些长~~
```cpp
#include <bits/stdc++.h>
using namespace std;
const int maxn=50005;
int n,q,x[maxn],sta[maxn],top;
vector<int> a,b,res;
inline int query(int x,int y)
{
printf("%d %d\n",x,y);
fflush(stdout);
int res;
scanf("%d",&res);
return res;
}
inline void end()
{
puts("-1");
fflush(stdout);
}
inline void solve()
{
a.clear(),b.clear(),res.clear();
scanf("%d%d",&n,&q);
a.push_back(0);
int lst=0,val=1;
for(int i=1;i<n;i++)
if(!query(lst,i)) val++,a.push_back(i);
else
{
val--,b.push_back(i);
if(val==0)
{
b.pop_back();
for(int i=0;!a.empty() || !b.empty();i++)
if(!(i&1)) res.push_back(a.back()),a.pop_back();
else res.push_back(b.back()),b.pop_back();
lst=i,val=1,a.push_back(i);
}
}
while(!a.empty() && !b.empty())
res.push_back(a.back()),res.push_back(b.back()),a.pop_back(),b.pop_back();
if(res.empty()) { end(); return; }
int sz=(int)res.size();
for(int i=0;i<sz;i++) x[i]=query(res[i],a.back());
int ans=x[0]+x[sz-1];
for(int i=0;i<sz-1;i++) ans+=(x[i] && x[i+1]);
if(ans<(int)a.size()) { end(); return; }
top=0;
if(!a.empty() && x[0])
sta[top++]=a.back(),a.pop_back();
for(int i=0;i<sz-1;i++)
{
sta[top++]=res[i];
if(!a.empty() && x[i] && x[i+1])
sta[top++]=a.back(),a.pop_back();
}
sta[top++]=res[sz-1];
if(!a.empty() && x[sz-1])
sta[top++]=a.back(),a.pop_back();
printf("%d\n",top);
for(int i=0;i<top;i++)
printf("%d%c",sta[i],i==top-1?'\n':' ');
fflush(stdout);
}
int main()
{
int T;
scanf("%d",&T);
while(T--) solve();
return 0;
}
```