题解:CF2237G Send GCDs

题意:这是一道通信题。第一次,题目会给 A 一个长为 \(n\) 的序列 \(a\),A 需要通过一定方式让 B 知道这个序列,他可以传给 B 一个长为 \(k\) 序列 \(b\)\(k\) 是你自己决定的。第二次,B 只得知 A 给出序列长度和 \(a\) 的长度,他可以询问交互库 \(b\)\(i\not= j,\gcd(i,j)\) 的值。B 最后要还原序列 \(a\)

交互限制:\(n\le 1000,a_i,b_i\le 10^6,k\le \lceil \frac{10n}{9}\rceil + 150\),B 的询问次数不能超过 \(180n+150\)

做法:

首先有一个朴素的想法,我们往序列中塞所有质因子 \(\le 10^6\) 的最大幂次,然后最后每次询问一下第 \(i\) 个数和别的质因子的 \(\gcd\) 乘起来就可以确定。但是这样 A 对了,B 完全不对,质因子太多了,比如我全放大质数你不可能把所有质数都丢一遍,如果质因子少一点那就可以做了。

看到 \(k\) 多出来 \(150\),我们考虑拉出来前 \(150\) 个质数,同时搜出来最大质因子要在这些质数里面的数在 \(10^6\) 内的个数,发现大概有 30 多万个,称这些数为好数,距离我们的 \(10^6\) 还有一些,直接做数全都是大质数就完全没法做,因为你为了区分你一定得往 \(b\) 中丢至少两个这个数。我们做一个神秘操作,我们把 \(a\) 全写成二进制,然后硬切开,按 \(18\) 位一段划分,这样我们的值域就变成 \(2^{18}\) 了,我们就映射到前面的好数里去,最后再往 \(b\) 里丢前 \(150\) 个质数的最高幂次,这样 \(k\) 的限制就对了,B 就对应询问解码出二进制串再重新算就可以了。

代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 5, N = 1e6;
int prime[maxn], tot, vis[maxn], n = 1000, mx[maxn];
void prepare() {
	vis[0] = vis[1] = 1;
	for (int i = 2; i <= n; i++) {
		if(!vis[i])
			prime[++tot] = i;
		for (int j = 1; j <= tot && prime[j] * i <= n; j++) {
			vis[i * prime[j]] = 1;
			if(i % prime[j] == 0)
				break;
		}
	}
	tot = min(tot, 150);
	for (int j = 1; j <= tot; j++) {
		mx[j] = prime[j];
		while(mx[j] * prime[j] <= N)
			mx[j] = mx[j] * prime[j];
	}
}
int use[N + 5];
void dfs(int d, int x) {
	use[x] = 1;
	if(d > tot)
		return ;
	for (int j = 1; j <= mx[d] && x * j <= N; j *= prime[d])
		dfs(d + 1, x * j);
}
int id[maxn], idx, a[maxn], rev[maxn];
void solve_first() {
	cin >> n; 
	string s;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		for (int j = 0; j <= 19; j++)
			s += char('0' + (a[i] >> j & 1));
	}
	while(s.size() % 18)
		s += '0';
	cout << s.size() / 18 + 150 << endl;
	int nw = 0;
	for (int i = 1; i <= s.size() / 18; i++) {
		int x = 0;
		for (int j = 0; j < 18; j++) {
			x += (1 << j) * (s[nw] - '0');
			nw++;
		}
		cout << id[x] << " ";
	}
	for (int i = 1; i <= 150; i++)
		cout << mx[i] << " ";
	cout << endl;
}
int query(int x, int y) {
	cout << "? " << x << " " << y << endl;
//	x = __gcd(a[x], a[y]);
	cin >> x; 
	return x;
}
int k;
void solve_second() {
	cin >> n >> k;
//	for (int i = 1; i <= k; i++)
//		cin >> a[i];
	string s;
	for (int i = 1; i <= k - 150; i++) {
		int res = 1;
		for (int j = k - 150 + 1; j <= k; j++) 
			res = res * query(i, j);
		res = rev[res];
		for (int j = 0; j < 18; j++)
			s += char('0' + (res >> j & 1));
//		cout << a[i] << " " << res << endl;
	}
//	cout << s << endl;
	cout << "! ";
	int nw = 0;
	for (int i = 1; i <= n; i++) {
		int x = 0;
		for (int j = 0; j <= 19; j++)
			x += (s[nw] - '0') * (1 << j), nw++;
		cout << x << " ";
	}
	cout << endl;
}
int main() {
//	freopen("test.in", "r", stdin);
//	freopen("std.out", "w", stdout);
	prepare();
	dfs(1, 1);
	for (int i = 1; i <= N; i++)
		if(use[i])
			id[idx++] = i, rev[i] = idx - 1;
//	cout << id[1] << endl;
	string s; cin >> s;
	if(s == "first") {
		int T; cin >> T;
		while(T--)
			solve_first();
	}
	else {
		int T; cin >> T;
		while(T--)
			solve_second();
	}
	return 0;
}
posted @ 2026-06-22 18:36  LUlululu1616  阅读(27)  评论(0)    收藏  举报