Codeforces Round 884 (Div. 1 + Div. 2) B. Permutations & Primes

发布时间 2023-10-18 20:47:53作者: zsxuan

给一个正整数 \(n\) ,你需要构造一个 \(n\) 的排列 \(p_1, p_2, \cdots, p_n\) 。对于排列 \(p\) 的每个子段 \([l, r]\)\(mex_{i = l}^{r} a_i\) 的结果为质数的次数尽可能多。

此处的 \(mex\) 最小排除值最低为 \(1\) 而非 \(0\)

不难想到,小质数 \(2, 3\) 容易构造。

于是有贪心算法:让 \(1\) 在尽可能多的子段里,而 \(2\)\(1\) 尽可能远。于是可以构造出尽可能多的 \(mex = 2\) 的子段。再让 \(3\)\(2\) 尽可能远,于是可以构造出尽可能多的 \(mex = 3\) 的子段。

要使 \(1\) 在尽可能多的子段里,\(1\) 应该在 \(\lceil \frac{n}{2} \rceil\) 位置。

  • 根据第 \(i\) 个元素会在 \((i - 0) \times (n - i + 1)\) 个子段中的性质

然后让 \(2\)\(1\) 位置,\(3\)\(n\) 位置。

此时可以观察出,除了 \([l, r]\) 这一段的 \(mex = 4\) ,所有段的 \(mex \leq 3\) 。于是上述的贪心算法可以在构造完 \(3\) 后截止。

view
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
void solve(){
	int n; std::cin >> n;
	if (n == 1) std::cout << "1\n";
	else if (n == 2) std::cout << "1 2\n";
	else {
		std::vector<int> a(n+1);
		a[(n+1)/2] = 1;
		a[1] = 2;
		a[n] = 3;
		int cur = 4;
		for (int i = 1; i <= n; i++) if (!a[i]) a[i] = cur++;
		for (int i = 1; i <= n; i++) std::cout << a[i] << " \n"[i==n];
	}
}
		                
int main() {
	#ifndef ONLINE_JUDGE
    freopen("IO/in", "r", stdin); freopen("IO/out", "w", stdout);
	#endif 
	int _ = 1; std::cin >> _;
	while (_--) solve();
	return 0;
}