题意
要求构造一个长度为 n n n 的序列 a a a,使得:
- ∀ i ∈ [ 1 , n ] , 1 ≤ a i ≤ 3 ⋅ 1 0 5 \forall i \in [1,n], \; 1 \leq a_i \leq 3 \cdot 10^5 ∀i∈[1,n],1≤ai≤3⋅105
-
∀
1
≤
i
<
j
≤
n
−
1
,
a
i
⋅
a
i
+
1
≠
a
j
⋅
a
j
+
1
\forall 1 \leq i < j \leq n - 1, \; a_i \cdot a_{i + 1} \neq a_j \cdot a_{j + 1}
∀1≤i<j≤n−1,ai⋅ai+1=aj⋅aj+1
要求最终序列中数字的种类最少
思路
不难发现,所有的数字选用质数一定最优,因为这样可以保证: a i ⋅ a i + 1 = a j ⋅ a j + 1 ⇔ ( a i , a i + 1 ) = ( a j , a j + 1 ) a_i \cdot a_{i + 1} = a_j \cdot a_{j + 1} \Lrarr (a_i, a_{i + 1}) = (a_j, a_{j + 1}) ai⋅ai+1=aj⋅aj+1⇔(ai,ai+1)=(aj,aj+1)
那么我们只需要保证每一对无序对 ( a i , a i + 1 ) (a_i, a_{i + 1}) (ai,ai+1) 均不同即可。
这里可以建模成一张图:以每一个值为顶点,边表示无序对(即相邻的数),每一个点还有一个自环(表示 ( a i , a i ) (a_i, a_i) (ai,ai)这样的无序对)
那么问题就转化为了,我们要选择最少的顶点数量,使得 无向完全图 中存在一条长度至少为 n − 1 n - 1 n−1,且不经过重复边的路径。
如果图上点数已经确定,考虑如何计算最长欧拉路径的长度:
- 如果 n n n 是奇数,那么每个点的度数是 n − 1 + 2 = n + 1 n - 1 + 2 = n + 1 n−1+2=n+1 为偶数,存在欧拉回路,因此最长欧拉路径的长度即为: C n 2 + n = ( n + 1 ) ⋅ n 2 C_n ^ 2 + n = \dfrac{ (n + 1) \cdot n}{2} Cn2+n=2(n+1)⋅n
- 如果 n n n 是偶数,那么每个点的度数就是奇数了,我们要考虑如何删去最少的边,使得图上恰好剩余 2 2 2 个奇度顶点,这样子就存在最长的欧拉路径;观察发现:删去一条边最多可以影响 2 2 2 个点的度数,因此保留两个奇度顶点最少需要删除: n − 2 2 \dfrac{n - 2}{2} 2n−2 条边。我们可以删除 ( 2 , 3 ) , ( 4 , 5 ) , . . . , ( n − 2 , n − 1 ) (2, 3), \; (4,5), \; ... \;, (n - 2, n - 1) (2,3),(4,5),...,(n−2,n−1) 这些边,这样子 1 1 1 号点就是奇度顶点,我们可以直接从 1 1 1 开始 d f s dfs dfs
最后我们只需要二分找到边数足够的 n n n,在上面跑一个欧拉路径即可
#include<bits/stdc++.h>
#define fore(i,l,r) for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
#define ull unsigned long long
#define ALL(v) v.begin(), v.end()
#define Debug(x, ed) std::cerr << #x << " = " << x << ed;
const int INF=0x3f3f3f3f;
const long long INFLL=1e18;
typedef long long ll;
std::vector<int> minp;
std::vector<int> primes;
void sieve(int n){
minp.assign(n + 5, 0);
primes.clear();
fore(i, 2, n + 1){
if(!minp[i]){
minp[i] = i;
primes.push_back(i);
}
for(auto p : primes){
if(p * i > n) break;
minp[i * p] = p;
if(minp[i] == p) break;
}
}
}
const int N = 2000005;
std::vector<int> stk;
bool vis[N];
std::vector<std::pair<int, int>> g[10005];
void dfs(int u){
for(auto [v, id] : g[u])
if(!vis[id]){
vis[id] = true;
dfs(v);
}
stk.push_back(primes[u]);
}
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
sieve(N);
int t;
std::cin >> t;
while(t--){
int n;
std::cin >> n;
auto check = [](int x, int n) -> bool {
if(x & 1) return x * (x - 1) / 2 + x >= n - 1;
return x * (x - 1) / 2 - (x - 2) / 2 + x >= n - 1;
};
int num = 0;
int l = 1, r = 10000;
while(l <= r){
int mid = l + r >> 1;
if(check(mid, n)){
num = mid;
r = mid - 1;
}
else l = mid + 1;
}
fore(u, 0, num) g[u].clear();
int tot = 0;
fore(i, 0, num)
fore(j, i, num){
if(num % 2 == 0 && (i & 1) && j == i + 1) continue; //偶数个点时,去掉一些边构造欧拉路径
g[i].push_back({j, ++tot});
g[j].push_back({i, tot});
}
fore(i, 0, tot + 1) vis[i] = false;
stk.clear();
dfs(0);
std::reverse(ALL(stk));
fore(i, 0, n) std::cout << stk[i] << " \n"[i == n -1];
}
return 0;
}