CF1634D Finding Zero
原题链接 每日一题系列。
在做过 CF1762D GCD Queries 后发现这个题跟那个 trick 一模一样,所以切了,尽管做那道的时候没有想出来。
每次任意选出四个数 \(a,b,c,d\),规定 \(a\leq b\leq c\leq d\)。考虑询问这四个数中所有组合。然后仅保留可能为 \(0\) 的位置,剩下的删掉。
\(f(a,b,c)=c-a,f(a,b,d)=d-a,f(a,c,d)=d-a,f(b,c,d)=d-b\)。
发现 \(d-a\geq c-a\),当 \(c=d\) 时取等;\(d-a\geq d-b\),当 \(a=b\) 时取等;所以这四个结果中会有 \(2\sim 4\) 个最大值。
当最大值个数为 \(2\),此时 \(a\not=b,c\not=d\)。我们的目的是保留 \(a\),因为 \(a\) 最小,只有 \(a\) 有可能是 \(0\)。实际我们无法分辨 \(a\) 和 \(d\),所以均保留,即保留在这两个最大值对应情况中都出现的数。
当最大值个数为 \(3\),此时有两种情况。当 \(a=b,c\not=d\)。因为 \(0\) 只有一个,所以 \(a\) 也不会是 \(0\),我们不需要保留任何数。当 \(a\not=b,c=d\)。我们的目的仍是保留 \(a\),此时 \(a\) 是在三个最大值对应情况中都出现的数。实际我们无法分辨到底是哪两个数相等,所以无论究竟是哪种情况都保留在三个最大值对应情况中都出现的数。
当最大值个数为 \(4\),此时 \(a=b,c=d\),一个都不需要保留。
所以总结起来就是每次保留在每个不为最大值对应情况中没出现的数。每次最少删掉两个,最多操作步数为 \(\lceil\dfrac{n-2}{2}\rceil\times 4\),刚好卡上界。
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=3010;
int T,n,p[MAXN],tot;
inline void add(int k)//加回来一些数直到够 k 个
{
for(int i=1;tot<k;++i)
{
bool flag=true;
for(int j=1;j<=tot;++j)
flag&=(p[j]!=i);
if(flag) p[++tot]=i;
}
}
inline void work()
{
cin>>n;tot=n;
for(int i=1;i<=n;++i) p[i]=i;
while(tot>2)
{
int a[5],b[5],MAX=-1;add(4);//不够 4 个要加到 4 个
for(int i=1;i<=4;++i) a[i]=p[tot--];
for(int i=1;i<=4;++i)
{
cout<<"? ";
for(int j=1;j<=4;++j)
if(j!=i) cout<<a[j]<<' ';
cout<<endl;cin>>b[i];MAX=max(MAX,b[i]);
}
for(int i=1;i<=4;++i)
if(b[i]!=MAX) p[++tot]=a[i];
}
add(2);cout<<"! "<<p[1]<<' '<<p[2]<<endl;
}
int main()
{
cin>>T;while(T--) work();
return 0;
}