题解【CF1937C. Bitwise Operation Wizard】

CF1937C. Bitwise Operation Wizard

好玩的交互。

题意:
给定 \(0,1,\dots,n-1\) 的一个排列 每次可以选取四个数 \(0 \leq a,b,c,d \leq n-1\),查询 \((p_a~|~p_b)\)\((p_c~|~p_d)\) 之间的大小关系。要求在 \(3n\) 次查询内找到两个数 \(i,j\),使得 \(p_i \oplus p_j\) 最大。

首先考虑如何使两个数的异或最大。根据位运算的性质,我们不难得出:

\[\begin{equation} \max_{0\leq i,j <n}\left\{i \oplus j\right\} = 2^k-1\quad(2^{k-1} \leq n < 2^k) \end{equation} \]

\(i,j\) 的第 \(1 \sim k\) 位都不相同时取到该最大值。

\[\begin{equation} \max_{0\leq i,j <n}\left\{i~|~j\right\} = 2^k-1\quad(2^{k-1} \leq n < 2^k) \end{equation} \]

\(i,j\) 的第 \(1 \sim k\) 位都不为 \(0\) 时取到该最大值。可以推出 \((1)\) 式取到最大值时 \((2)\) 式也取到最大值。

因为我们必然要使答案的第 \(k\) 位为 \(1\),那么我们要先找到一个第 \(k\) 位为 \(1\) 的数。因为我们能够查询大小关系,所以我们考虑找到排列中最大的一个数即 \(n-1\) 的位置。

\(a~|~a=a\) 可知,比较 \((p_a~|~p_a)\)\((p_b~|~p_b)\) 的大小关系等价于查询 \(p_a\)\(p_b\) 的大小关系。所以我们可以很容易的通过这样 \(n-1\) 次比较找到最大值的位置。

接下来考虑找到数 \(t=(2^k-1)\oplus(n-1)\) 的位置。容易知道 \(t\oplus(n-1)=t~|~(n-1)=2^k-1\)
\(t\)\(n-1\) 满足 \((1)(2)\) 两式的最大值条件。而在满足 \(x~|~(n-1)=2^k-1\)\(x\) 中,\(t\) 一定是最小的那个数(因为在 \(n-1\)\(1\) 的位上 \(t\) 一定取 \(0\))。

所以我们考虑取出使得 \((n-1)|p_i\) 取最大值的所有位置,然后在这些位置中找到最小的数的位置,这个位置的数就是 \(t\)。可以知道比较的次数不会超过 \(2(n-2)\)

最后我们就在 \(3n\) 步内得到了 \(n-1\)\(t\) 的位置,输出即可。

以上针对 \(n \geq 4\),注意 \(n=2\)\(n=3\) 的边界情形。

inline char query(int p1_1,int p1_2, int p2_1,int p2_2)
{
	cout<<"? "<<p1_1-1<<' '<<p1_2-1<<' '<<p2_1-1<<' '<<p2_2-1<<endl;
	char op; cin>>op;
	return op; 
}
inline int ans_out(int p1,int p2)
{
	cout<<"! "<<p1-1<<' '<<p2-1<<endl;
	return 0;
} 
 
inline int Solve()
{
	#ifdef DEBUG
	printf("Debuging...\n");
	#endif
 
	cout.flush();
	cin>>n;
	char op;
	if(n==2) return ans_out(1,2);
	if(n==3)
	{
		op=query(1,2, 1,3);
		int k=(op=='>')?2:3;
		op=query(1,k, k,k^1);
		int l=(op=='>')?1:k^1;
		return ans_out(k,l);
	}
	
	int k=1;
	for(int i=2;i<=n;i++)
	{
		op=query(k,k, i,i);
		if(op=='<') k=i; 
	}
	
	vector<int> com;
	int l=(k==1)?2:1;
	com.push_back(l);
	for(int i=1;i<=n;i++)
	{
		if(i==k || i==l) continue;
		op=query(k,l, k,i);
		if(op=='<')
		{
			vector<int>().swap(com);
			l=i;
			com.push_back(l);
		}
		else if(op=='=')
			com.push_back(i);
	}
	l=com[0];
	for(int i=1;i<com.size();i++)
	{
		op=query(l,l, com[i],com[i]);
		if(op=='>') l=com[i];	
	}
	ans_out(k,l);
 
	return 0;
}
posted @ 2024-03-05 19:40  Coinred  阅读(7)  评论(0编辑  收藏  举报