CF1634D - Finding Zero(构造性算法 + 数学规律 + 交互题 / 铁牌级)

1634D - Finding Zero(源地址自⇔CF1634D


tag

⇔构造性算法、⇔数学规律、⇔交互题、⇔铁牌级(*2000)

题意

有一个长度为 \(N\) 的序列,其中包含一个 \(0\) ,你需要通过询问得到信息,找到这个 \(0\) 所在的位置。规定操作如下:

  • 你至多可以询问 \(2*N-2\) 次;
  • 对于每次询问,你需要给出三个数字 \(i,j,k\) ,然后你会得到 \(max(a_i,a_j,a_k)−min(a_i,a_j,a_k)\) 的值。

思路

官方思路(数学规律)

对于任意四个位置,我们都可以通过四次查询找到其中肯定不是 \(0\) 的两个位置:不妨假设有 \(A,B,C,D\) 四个位置,对应的数字为 \(a,b,c,d\) ,满足 \(a=0,b<c<d\) ,那么查询结果如下:

\[\left\{\begin{matrix} \bar{a} & = & max(b,c,d)-min(b,c,d) & =d-b \\ \bar{b} & = & max(a,c,d)-min(a,c,d) & =d \\ \bar{c} & = & max(a,b,d)-min(a,b,d) & =d \\ \bar{d} & = & max(a,b,c)-min(a,b,c) & =c \\ \end{matrix}\right.\]

我们可以发现,查询结果最大的两个数字( \(b\)\(c\) )必定不是 \(0\) 。相似的,对于另外四个非零的数字,这一规律同样适用。

至此,我们得到规律:每次查询四个位置,丢掉结果最大的两个,然后引入两个未查询的位置,继续这一循环,直到查询完所有的位置。特别的,对于 \(N\) 为奇数的情况,我们会遇到未查询的位置不满两个的情况,这时候我们只需要引入一个此前丢掉的位置即可继续这一循环。

我们可以通过证明复杂度来确保该算法无误:当 \(N\) 为偶数时,至多 \(\frac{n-2}{2} * 4=2*n-4\) 次询问,当 \(N\) 为奇数时,至多 \(\frac{n-3}{2} * 4+4=2*n-2\) 次询问,均满足题意。

AC代码(数学规律)

点击查看代码
//====================
int n;
tuple<int, int, int, int> Ready;
pair<int, int> Pre;
//====================
void check() {
	auto [aa, bb, cc, dd] = Ready;
	int a, b, c, d;
	
	cout << "? " << bb << " " << cc << " " << dd << endl; cout.flush();
	cin >> a;
	cout << "? " << aa << " " << cc << " " << dd << endl; cout.flush();
	cin >> b;
	cout << "? " << aa << " " << bb << " " << dd << endl; cout.flush();
	cin >> c;
	cout << "? " << aa << " " << bb << " " << cc << endl; cout.flush();
	cin >> d;
	
	if(max(a, max(b, max(c, d))) == a) {
		if(max(b, max(c, d)) == b) Pre = {cc, dd};
		if(max(b, max(c, d)) == c) Pre = {bb, dd};
		if(max(b, max(c, d)) == d) Pre = {bb, cc};
	}else if(max(a, max(b, max(c, d))) == b) {
		if(max(a, max(c, d)) == a) Pre = {cc, dd};
		if(max(a, max(c, d)) == c) Pre = {aa, dd};
		if(max(a, max(c, d)) == d) Pre = {aa, cc};
	}else if(max(a, max(b, max(c, d))) == c) {
		if(max(a, max(b, d)) == a) Pre = {bb, dd};
		if(max(a, max(b, d)) == b) Pre = {aa, dd};
		if(max(a, max(b, d)) == d) Pre = {aa, bb};
	}else if(max(a, max(b, max(c, d))) == d) {
		if(max(a, max(b, c)) == a) Pre = {bb, cc};
		if(max(a, max(b, c)) == b) Pre = {aa, cc};
		if(max(a, max(b, c)) == c) Pre = {aa, bb};
	}
}
void Solve() {
	cin >> n;
	Pre = {1, 2};
	FOR2(i, 3, n - 1) {
		Ready = {Pre.fi, Pre.se, i, i + 1}; //备选项为上一轮留下的两个位置+新加入的两个位置
		check();
	}

	if(n % 2 == 1) { //引入一个此前已经丢掉的位置
		vector<int> add;
		if(Pre.fi != 1 && Pre.se != 1) add.pb(1);
		if(Pre.fi != 2 && Pre.se != 2) add.pb(2);
		if(Pre.fi != 3 && Pre.se != 3) add.pb(3);
		Ready = {Pre.fi, Pre.se, n, add[0]};
		check();
	}
	
	cout << "! " << Pre.fi << " " << Pre.se << endl; cout.flush();
}

错误次数


文 / WIDA
2022.02.22 成文
首发于WIDA个人博客,仅供学习讨论


更新日记:
2022.02.22 成文


posted @ 2022-02-22 22:04  hh2048  阅读(65)  评论(0编辑  收藏  举报