Gym-106648D [CCF CAT NAEC 2026] 卡牌接龙 题解
Gym-106648D [CCF CAT NAEC 2026] 卡牌接龙
对于一个整数序列 \(a_1,a_2,\cdots,a_m\) 中的位置 \(i\)(\(2\le i\le m\)),如果 \(a_{i-1}\) 整除 \(a_i\) 或 \(a_i\) 整除 \(a_{i-1}\),则称位置 \(i\) 是「Kirakira」的;特别地,规定位置 \(1\) 不是「Kirakira」的。
现在有 \(n\cdot k\) 个数,其中 \(1,2,\cdots,n\) 各有 \(k\) 个,且 \(k=\pi(n)\),即 \(k\) 为 \(1,2,\cdots,n\) 中的质数个数。
需要将这 \(n\cdot k\) 个数排成一个序列,使得「Kirakira」的位置尽可能多,请你构造并输出一个这样的序列。
\(2\le n\le5000\)。
设 \(x\) 和 \(y\) 的标准质因数分解为 \(x=\prod_{i=1}^\infty p_i^{\alpha_1}\),\(y=\prod_{i=1}^\infty p_i^{\beta_i}\),其中 \(\alpha_i,\beta_i\ge0\),那么 \(x\mid y\) 当且仅当 \(\alpha_i\le\beta_i\) 对所有 \(i=1,2,\cdots\) 都成立。
为了最大化利用这些数,容易想到序列的形式类似于 \(a_i,p_1a_i,a_i,p_2a_i,\cdots\)。
并且,注意到一个关键性质:由于 \(x\mid x\),如果这样构造之后 \(a_i\) 仍有剩余,可以将多余的 \(a_i\) 放在任何一个其已经出现的位置上。
那么,我们可以考虑让每个数出现次数尽可能不重复。
将乘一个质数 \(p\) 视为在图上走一步(有点类似于 AtCoder-arc084_b Small Multiple),即 \(x\to px\),那么为了保证 \(px\) 是新的点,一定要满足 \(p\ge\)「\(x\) 的最大质因数」。
这样一定不会重复经过点,那么就构成一棵树,根节点为 \(1\)。一个「Kirakira」位置最多的序列就是这棵树的一个欧拉序 (Euler tour)。
在欧拉序中,设节点 \(v\) 的子节点数量为 \(soncnt(v)\),则 \(v\) 出现的次数 \(c_v=soncnt(v)+1\)。
- 对于 \(v=1\),\(c_1=\pi(n)+1\),所以我们只要去掉最后一个 \(1\) 即可。
- 对于 \(v=2,3,\cdots\),由于子节点 \(pv\le n\),一定有 \(p\le n/v\),故可用的 \(p\) 的数量 \(\le\pi(\lfloor n/v\rfloor)\le\pi(\lfloor n/2\rfloor)<\pi(n)\)(对于 \(n\ge2\),在区间 \([\lfloor n/2\rfloor+1,n]\) 中至少有一个质数),从而 \(c_v=soncnt(v)+1<\pi(n)+1\),即 \(c_v\le\pi(n)\)。因此对于 \(v\ne1\) 都是够用的。
综上,我们得到了一种序列,使得每一对相邻位置都是「Kirakira」的。
#include <bits/stdc++.h>
#define f(i, a, b) for (int i = (a); i <= (b); ++i)
using namespace std;
typedef vector<int> vi;
typedef vector<vector<int> > vvi;
void solve() {
int n, k; cin >> n >> k;
vvi e(n + 1);
vi pri, maxp(n + 1);
bitset<5001> vis; // not prime
vis[1] = true;
f(i, 2, n) {
if (!vis[i]) {
pri.push_back(i);
maxp[i] = i;
e[1].push_back(i);
}
for (int j: pri) {
if (i * j > n) break;
vis[i * j] = true;
maxp[i * j] = maxp[i];
e[i * j / maxp[i]].push_back(i * j);
if (i % j == 0) break;
}
}
vi rem(n + 1, k);
vi tour;
auto dfs = [&](auto&& self, int u) -> void {
tour.push_back(u);
--rem[u];
for (int v: e[u]) {
self(self, v);
tour.push_back(u);
--rem[u];
}
};
dfs(dfs, 1);
tour.pop_back();
for (int x: tour) {
cout << x << ' ';
while (rem[x] > 0) {
cout << x << ' ';
--rem[x];
}
}
cout << '\n';
return;
}
signed main() {
cin.tie(0)->sync_with_stdio(false);
int tt = 1;
// cin >> tt;
while (tt--) solve();
return 0;
}

浙公网安备 33010602011771号