[CF1491F] Magnets

Problem

你有 (n) 个磁铁,每个磁铁有属性,为下列三种之一

  • NS-(表示无磁性)

一次操作你可以选择不相交的两个磁铁的子集 (S)(T),假设 (S) 中有 (n_1)N(s_1)S(T) 中有 (n_2)N(s_2)S。这些你都不知道。通过交互库你可以得到 (n_1n_2-n_1s_2-n_2s_1+s_1s_2)(化简得到:((n_1-s_1)(n_2-s_2)) )。得到的数值可能 (<0)

你可以花费不超过 (n+lfloorlog_2n floor) 次操作找出所有没有磁性的磁铁。

多组数据 (Tleq 100),每次 (nleq 2000)(sum nleq 2000)保证至少两个有磁极的磁铁和一个无磁极的磁铁

Sol

如果我们能找到一个有磁极的磁铁,那么问题将非常简单。

然而要想找出一个有磁极的磁铁,必须借助另外至少一个有磁极的磁铁。

假象一种极端的边界:如果只有两块磁铁有磁极怎么办?只能硬着头皮一个一个找呗。

于是得到一种方案:每次查询 (S={i})(T={1,cdots,i-1})。当遇到第二块有磁极的磁铁之前,答案一定为 (0)(因式有一项必为 (0))。当 (i) 是第二块有磁极的磁铁时,答案会变成 (pm 1),以此我们就能找出一块有磁极的磁铁了!

利用这块磁铁找出 (>i) 位置的无磁性的磁铁,这样总查询次数为 (n-1)

但是第一块有磁性的磁铁我们还没有找到,如果找到了剩下的都是无磁性的磁铁。这时不必要一次一次找,观察题目使用技巧知道要二分找出来。于是继续构造 (S={i})(T={1,cdots,j}) 来判断前缀 (j) 的磁铁中有没有有磁性的磁铁,然后二分,这部分查询次数为 (lceillog_2n ceil)

综上,总次数 (=n-1+lceillog_2n ceilleq n+lfloorlog_2n floor)。时间复杂度为 (mathcal O(Tn^2))

Code

#include <bits/stdc++.h>
using std::vector;
const int N = 2005;
int T, n, F;
int main() {
	scanf("%d", &T);
	while (T--) {
		scanf("%d", &n);
		for (int i = 2; i <= n; i++) {
			printf("? 1 %d
%d
", i - 1, i);
			for (int j = 1; j < i; j++) printf("%d ", j);
			puts(""); fflush(stdout);
			scanf("%d", &F);
			if (F) {
				vector<int> ans;
				for (int j = i + 1; j <= n; j++) {
					printf("? 1 1
%d
%d
", i, j);
					fflush(stdout);
					scanf("%d", &F);
					if (!F) ans.push_back(j);
				}
				int l = 1, r = i - 1;
				while (l <= r) {
					int mid = l + r >> 1;
					printf("? 1 %d
%d
", mid, i);
					for (int j = 1; j <= mid; j++) printf("%d ", j);
					puts(""); fflush(stdout);
					scanf("%d", &F);
					if (F) r = mid - 1; else l = mid + 1;
				}
				for (int j = 1; j < i; j++)
					if (j != r + 1) ans.push_back(j);
				printf("! %d ", ans.size());
				for (int j = 0; j < ans.size(); j++) printf("%d ", ans[j]);
				puts(""); fflush(stdout); break;
			}
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/ac-evil/p/14462322.html