题解: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;
}

浙公网安备 33010602011771号