抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

告诉你 nn,要你猜一个长度为 nn 的正整数序列 {ai}\left\{a_i\right\}aia_i 两两不同,且存在 0l2×105n0\le l\le 2\times 10^5-n 使得 ai(l,l+n]a_i\in\left(l,l+n\right]

每次你可以向交互库询问 ? x yxyx\neq y),交互库向你返回 lcm(ax,ay)\mathrm{lcm}(a_x,a_y)。询问次数为 n+5000n+5000,其中 3n1053\le n\le 10^5

我们先考虑 nn 比较小的情况,这时候我们可以考虑到我们有那个 50005000,我们考虑 n2n^2 次询问。我们考虑利用一个柿子,也就是当 gcd(x,y)=1\gcd(x,y)=1 时,gcd(lcm(x,z),lcm(y,z))=gcd(xzgcd(x,z),yzgcd(y,z))=zgcd(xgcd(x,z),ygcd(y,z))=z\gcd(\mathrm{lcm}(x,z),\mathrm{lcm}(y,z))=\gcd(\frac{xz}{\gcd(x,z)},\frac{yz}{\gcd(y,z)})=z\cdot\gcd(\frac{x}{\gcd(x,z)},\frac{y}{\gcd(y,z)})=z,且有 x,yN+\forall x,y\in \N_+zgcd(lcm(x,z),lcm(y,z))z\mid\gcd(\mathrm{lcm}(x,z),\mathrm{lcm}(y,z))。又 nN+\forall n\in \N_+gcd(n,n+1)=1\gcd(n,n+1)=1。故当 n=4n=4 时,我们只要令 ai=gcdjilcm(ai,aj)a_i=\gcd_{j\neq i}\mathrm{lcm}(a_i,a_j) 即可。而当 n=3n=3 时,我们发现最大的那个询问结果其实是两个最大的相乘,也即最小值为 查询得到的最大值1\left\lfloor\sqrt{\text{查询得到的最大值}}\right\rfloor-1,且最小值的那个位置确定了,然后另外两个直接与最小值取 lcm\mathrm{lcm} 之后对比一下就也确定了。

这种情况下,询问次数是 n(n1)2\frac{n\cdot(n-1)}{2},而 n(n1)25000+n\frac{n\cdot(n-1)}{2}\le 5000 + n 正好解得 n100n\le 100

这样我们就解决了 n100n\le 100 的情况,我们考虑 nn 更大的时候。首先我们会考虑到我们能否找到一个素数 pp,且这个素数能尽量大,最好能保证 lcm(p,ai)=pai\mathrm{lcm}(p,a_i)=p\cdot a_i。我们考虑能否找到 pn2p\ge \frac{n}{2},这样的话除了有一个 ai=2pa_i=2p 可能会存在 lcm(p,ai)=2p\mathrm{lcm}(p,a_i)=2p。之外,其余的 aia_i 都能直接使用 ai=lcm(p,ai)pa_i=\frac{\mathrm{lcm}(p,a_i)}{p} 得到。因此我们可以先把除了 lcm\mathrm{lcm}2p2p 的位置留下,其余位置查询之后,换一个不是 22 的素数判断那个位置是 22 还是 2p2p。我们考虑时候有这样的两个素数,大概估计 π(n))=nlnn\pi(n))=\frac{n}{\ln n},当 nn 较大的时候素数密度是越来越疏的。而 π(105)π(105100)=7.93\pi(10^5)-\pi(10^5-100)=7.93,也就是说应该是足够的,而事实证明当 n100n\ge 100 的时候确实是足够的。

然后我们考虑怎么找素数,我们考虑随机找一些位置并尝试将其确定下来。我们先大概看看应该需要找多少个位置,我们考虑由于素数密度越来越疏,我们就当是最大值是 10510^5,于是素数密度为 ρ(n)=π(105)π(105n)n\rho(n)=\frac{\pi(10^5)-\pi(10^5-n)}{n},考虑到越往里素数越密,我们仍然认为选到素数的概率最小值为 ρ(100)=0.08\rho(100)=0.08,其实发现还是挺大的。然后我们考虑如何确定一个数,我们仍然用上面 n2n^2 暴力的思路,我们考虑所随机几个数与要确定的这个数进行 lcm\mathrm{lcm},我们考虑随机两个数的素数概率为多大,我们令概率为 P\mathcal{P},那么我们发现如果随机两个数 gcd\gcdi(i2,iN+)i\,(i\ge 2,i\in\N_+) 的概率为 Pi2\frac{\mathcal{P}}{i^2},也就是要在 ni\frac{n}{i} 的空间里找到的 gcd\gcd11 的方案然后把区间拉长 ii 倍,由于是两个数所以除了 i2i^2。于是我们有 P=1i=lrPi2\mathcal{P}=1-\sum_{i=l}^r\frac{\mathcal{P}}{i^2}也即 P=11+i=lr1i2\mathcal{P}=\frac{1}{1+\sum_{i=l}^r\frac{1}{i^2}},期望尝试 1+i=lr1i21+\sum_{i=l}^r\frac{1}{i^2} 次,并不是一个很大的数。而我们又考虑生日悖论,因为我们是随机抽取多个数字,然后只要有其中两个 gcd\gcd11 即可,所以理论上即使是非洲酋长也应该是能过的。

由于附赠了 50005000 次询问次数,所以我们考虑选 249249 个数,每个数抽 2020 个数与之 lcm\mathrm{lcm} 之后求 gcd\gcd。剩下的 2020 次其实是给前面说的那个 2p2p 的特殊情况留的。然后最后我们一遍 O(n)\mathcal{O}(n) 询问即可。

由于交互题写了很多调试语句所以代码可能有点小长 qaq

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
typedef long long LL;

int n;

LL ask(int x, int y) {
assert(1 <= x && x <= n && 1 <= y && y <= n);
std::cout << "? " << x << ' ' << y << std::endl;
std::cout.flush();
LL ans;
std::cin >> ans;
return ans;
}

namespace solve_small {
const int maxn = 105;

LL lcm[maxn][maxn];

void solve() {
memset(lcm, 0, sizeof(lcm));
for (int i = 1; i <= n; ++i) {
for (int j = 1; j < i; ++j) {
lcm[i][j] = lcm[j][i] = ask(i, j);
}
}
std::pair<int, int> idans(0, 0);
if (n == 3) {
for (int i = 1; i <= n; ++i) {
for (int j = 1; j < i; ++j) {
if (lcm[i][j] > lcm[idans.first][idans.second]) {
idans = std::make_pair(i, j);
}
}
}
std::vector<int> ANS(4);
ANS[idans.first] = sqrt(lcm[idans.first][idans.second]);
ANS[idans.second] = ANS[idans.first] + 1;
ANS[6 - idans.first - idans.second] = ANS[idans.first] - 1;
if ((LL) ANS[idans.first] * ANS[6 - idans.first - idans.second]
/ std::__gcd(ANS[idans.first], ANS[6 - idans.first - idans.second])
!= lcm[idans.first][6 - idans.first - idans.second]) {
std::swap(ANS[idans.first], ANS[idans.second]);
}
std::cout << "! ";
for (int i = 1; i <= 3; ++i) {
std::cout << ANS[i] << ' ';
}
std::cout << std::endl;
std::cout.flush();
} else {
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
lcm[i][i] = std::__gcd(lcm[i][i], lcm[i][j]);
}
}
std::cout << "! ";
for (int i = 1; i <= n; ++i) {
std::cout << lcm[i][i] << ' ';
}
std::cout << std::endl;
std::cout.flush();
}
}
}

namespace solve_large {
struct debug {
int n;
std::vector<int> alb;

LL ask(int x, int y) {
return (LL) alb[x - 1] * alb[y - 1] / std::__gcd(alb[x - 1], alb[y - 1]);
}

debug() {
std::ifstream fin("a.in");
fin >> n;
for (int i = 1; i <= n; ++i) {
int xx;
fin >> xx;
alb.push_back(xx);
}
}
} deb;

#ifdef Debug
#define ask(x, y) deb.ask(x, y)
#endif

const int maxn = 200005;

LL ans[maxn];
int np[maxn];

std::mt19937 rnd(20030211u);

void Euler(const int n = 200000) {
std::vector<int> prime;
prime.clear();
for (int i = 2; i <= n; ++i) {
if (!np[i]) {
prime.push_back(i);
}
for (unsigned j = 0; j < prime.size() && (LL) i * prime[j] <= n; ++j) {
np[i * prime[j]] = true;
if (!(i % prime[j])) {
break;
}
}
}
}

void solve() {
std::vector<int> id;
id.clear();
for (int i = 1; i <= n; ++i) {
ans[i] = 0;
id.push_back(i);
}
std::shuffle(id.begin(), id.end(), rnd);
int qd = 0;
for (int i = 1; i <= 249 && i <= n; ++i) {
qd = id[i - 1];
for (int j = 1; j <= 20; ++j) {
int wz = rnd() % n + 1;
while (wz == qd) {
wz = rnd() % n + 1;
}
ans[qd] = std::__gcd(ask(qd, wz), ans[qd]);
if (ans[qd] <= 200000 && ans[qd] && !np[ans[qd]]) {
break;
}
}
if (ans[qd] && !np[ans[qd]] && ((ans[qd] << 1) > n)) {
break;
}
}
assert(ans[qd] <= 200000 && ans[qd] && !np[ans[qd]] && ((ans[qd] << 1) > n));
for (int i = 1; i <= n; ++i) {
if (!ans[i]) {
ans[i] = ask(i, qd) / ans[qd];
}
}
LL mx = 0;
int idd = 0;
for (int i = 1; i <= n; ++i) {
if (!np[ans[i]] && ans[i] >= mx) {
mx = ans[i];
idd = i;
}
}
for (int i = 1; i <= n; ++i) {
if (ans[i] == 2) {
ans[i] = ask(i, idd) / mx;
}
}
#ifndef Debug
std::cout << "! ";
#endif
for (int i = 1; i <= n; ++i) {
std::cout << ans[i] << ' ';
}
std::cout << std::endl;
std::cout.flush();
}

#ifdef Debug
#undef ask(x, y) deb.ask(x, y)
#endif
}

void solve() {
#ifndef Debug
std::cin >> n;
#else
n = solve_large::deb.n;
freopen("a.out", "w", stdout);
std::cout << n << std::endl;
#endif
if (n <= 100) {
solve_small::solve();
} else {
solve_large::solve();
}
}

int main() {
solve_large::Euler();
int T;
#ifndef Debug
std::cin >> T;
#else
T = 1;
#endif
while (T--) {
solve();
}
return 0;
}

评论