NOI 2025 见!

 

题解:CF2123F Minimize Fixed Points

前置知识、符号与约定

前置知识

符号与约定

文中用到的非公认或非常用的符号,按出现顺序排列:

  • \([n]\)\(\{1,2,3,\ldots ,n\}\)

  • \(A \setminus B\):集合 \(A\) 与集合 \(B\) 的差

    \(A \setminus B = \{x \mid x \in A \ \land \ x \not\in B \}\)

  • \(P(x)\)\(x\) 的最大质因子

  • \(a \not\perp b\)\(a\)\(b\) 不互质

  • \(\mathbb{P}\):质数集

  • \(\operatorname{supp}(\sigma)\):轮换 \(\sigma\) 的支撑集

    \(\sigma = (a_1\ a_2\ \cdots\ a_k)\),则 \(\operatorname{supp}(\sigma) = \{a_1, a_2, \dots, a_k\}\)

  • \(\sigma \circ \tau\):轮换 \(\sigma\) 与轮换 \(\tau\) 的乘积

    \(\operatorname{supp}(\sigma) \cap \operatorname{supp}(\tau) = \emptyset\)(文中仅出现了这种情况),设 $\sigma = (a_1 \cdots a_k) \(,\) \tau = (b_1 \cdots b_m) $ ,则 \(\sigma \circ \tau = (a_1 \cdots a_k)(b_1 \cdots b_m)\)

  • \(\prod\):轮换的连乘积

题意

给定 \(n\),构造一个 \([n]\) 上的置换 \(p\),满足:

  1. \(\forall\, i \in [n] \setminus \{1\},\quad p(i) \not\perp i\)
  2. 最小化 \(\sum_{i=1}^{n} [p(i) = i]\)

报告任意一个 \(p\)。可以证明,\(p\) 总是存在的。

思路

观察 1:\(\sum_{i=1}^{n} [p(i) = i]\) 的下界

注意到

\[\forall\, i \in \{1\} \cup \mathbb{P} \cap \left[\left\lceil \frac{n}{2} \right\rceil, n\right], \quad p(i) = i \]

无法避免的,因此这些位置已经被固定

证明

\(p(1) = 1\) 无法避免是因为 \(1\)任何数都互质。

\[\forall\, i \in \mathbb{P} \cap \left[\left\lceil \frac{n}{2} \right\rceil, n\right], \quad p(i) = i \]

无法避免是因为

\[\forall\, i \in \mathbb{P} \cap \left[\left\lceil \frac{n}{2} \right\rceil,\, n\right],\quad \forall\, j \in [n] \setminus \{i\},\quad i \perp j\]

因为最小的,不等于 \(x\) 的,与 \(x\) 不互质的正整数是 \(2x\)

因此, \(\sum_{i=1}^{n} [p(i) = i]\) 的下界为 \(\left|\{1\} \cup \mathbb{P} \cap \left[\left\lceil \frac{n}{2} \right\rceil, n\right]\right|\)

转换 1:最小化 \(\sum_{i= 1}^{n} [p(i) = i]\) 的另一种表示

\(p\) 的轮换分解为 \(C_1 C_2 \dots C_k\)

若存在 \(p\) 的轮换 \(\sigma\) 满足 \(\gcd(\operatorname{supp}(\sigma)) > 1\)\(|\sigma| > 1\),则有

\[\forall\, x \in \operatorname{supp}(\sigma),\quad p(x) \not\perp x\ \land\ p(x) \ne x \]

因此在

\[\forall\, i \in [n] \setminus \{1\}, \quad p(i) \not\perp i \]

的前提下,最小化

\[\sum_{i= 1}^{n} [p(i) = i] \]

可转换为,在

\[\forall\, i \in [k], \quad \gcd(\operatorname{supp}(C_i)) > 1 \]

的前提下最小化

\[\sum_{i= 1}^{k} \left[\left|C_i\right| = 1\right] \]

观察 2:下界可以被取到

证明

若有

\[\forall\, i \in [n] \setminus \left(\{1\} \cup \mathbb{P} \cap \left[\left\lceil \frac{n}{2} \right\rceil, n\right]\right), \ \text{包含}\ i \ \text{的轮换} \ \sigma \ \text{满足} \ \gcd(\operatorname{supp}(\sigma)) > 1 \ \land \ |\sigma| > 1 \]

则有下界可以被取到。

我们通过直接构造 \(p\) 来证明:

\[p = D_1 \circ \left(\prod_{i \in \mathbb{P} \cap [2, n]} D_i\right) \]

其中:

  • \(D_1 = (1)\) 为平凡轮换
  • \(\forall \, i \in \mathbb{P} \cap [2,n]\)\(D_i\) 为满足 \(\operatorname{supp}(D_i) = \{ x \in [2,n] \mid P(x) =i \}\) 的轮换

因为有

\[\forall\, i \in [n] \setminus \left(\{1\} \cup \mathbb{P} \cap \left[\left\lceil \frac{n}{2} \right\rceil, n\right]\right), \ \text{包含}\ i \ \text{的轮换} \ \sigma \ \text{满足} \ \gcd(\operatorname{supp}(\sigma)) \in \mathbb{P} \]

所以有

\[\forall\, i \in [n] \setminus \left(\{1\} \cup \mathbb{P} \cap \left[\left\lceil \frac{n}{2} \right\rceil, n\right]\right), \ \text{包含}\ i \ \text{的轮换} \ \sigma \ \text{满足} \ \gcd(\operatorname{supp}(\sigma)) > 1 \]

仍需证明

\[\forall\, i \in [n] \setminus \left(\{1\} \cup \mathbb{P} \cap \left[\left\lceil \frac{n}{2} \right\rceil, n\right]\right), \ \text{包含}\ i \ \text{的轮换} \ \sigma \ \text{满足} \ |\sigma| > 1 \]

注意到有

\[\forall\, i \in \mathbb{P} \cap \left[2, \left\lceil\frac{n}{2}\right\rceil\right ), \quad 2i \in [n] \ \land \ P(2i) = i \]

综上所述,\(p\) 符合题意要求,证毕。

算法&实现

算法

上述思路中,我们需要实现,将每个数按最大质因子“归类”。

对每个数因数分解以寻找其最大质因子,不是明智的做法。因为寻找因子是困难的,而寻找倍数是简单的。

以下伪代码展示了如何预处理每个 \(i \in [n]\) 的最大质因子。

P[1] = P[2] = ... = P[n] = 0
for (i : 从大到小遍历 [1, n] 以内质数) {
  for (j = i; j <= n; j += i) if (P[j] == 0) {
    P[j] = i;
  }
}

P[1], P[2], ..., P[n] 即为所求,时间复杂度 \(\Theta(n \log \log n)\),空间复杂度 \(\Theta(n)\)

代码

#include <bits/stdc++.h>
using namespace std;

constexpr int N = 100000 + 1;

bitset<N> np;
vector<int> primes;
int p[N], cyc[N];

void solve() {
	int n;
	cin >> n;
	for (auto itr = lower_bound(primes.begin(), primes.end(), (n + 1) >> 1, greater<>()); itr != primes.end(); ++itr) {
		int i = *itr, len = 0;
		for (int j = i; j <= n; j += i) if (!p[j]) {
			cyc[len++] = j;
		}
		if (len) {
			for (int j = 0; j < len; ++j) {
				p[cyc[j]] = cyc[j + 1];
			}
			p[cyc[len - 1]] = cyc[0];
		}
	}
	for (int i = 1; i <= n; ++i) {
		cout << (p[i] ? p[i] : i) << ' ';
	}
	cout << '\n';
	memset(p + 1, 0, sizeof(int) * n);
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	for (int i = 2; i * i < N; ++i) if (!np[i]) {
		for (int j = i * i; j < N; j += i) {
			np[j] = true;
		}
	}
	for (int i = 2; i < N; ++i) if (!np[i]) {
		primes.push_back(i);
	}
	reverse(primes.begin(), primes.end());

	int t;
	cin >> t;
	while (t--) {
		solve();
	}
	return 0;
}

复杂度分析

\(N\)\(O(\max_{1 \leq i \leq t}{n_i})\)

上述代码时间复杂度为 \(\Theta(N \log \log N + t \log \frac{N}{\log N} + \sum_{i=1}^t n_i \log \log n_i)\),空间复杂度为 \(\Theta(N)\)

注意,若

for (auto itr = lower_bound(primes.begin(), primes.end(), (n + 1) >> 1, greater<>()); itr != primes.end(); ++itr)

写成

for (auto itr = primes.begin(); itr != primes.end(); ++itr)

则时间复杂度退化成 \(\Theta(N \log \log N + t\frac{N}{\log N} + \sum_{i=1}^t n_i \log \log n_i)\)

posted on 2025-07-18 17:12  SkyWave2022  阅读(32)  评论(0)    收藏  举报

导航