Solution Set【2023.12.14】
CF698F Coprime Permutation
考虑 \(p_i = 0\) 的情况下怎么做,首先排列 \(p_i = i\) 一定符合条件,考虑在此基础上生成其他符合要求的排列,考虑什么情况下 \(p_i\) 和 \(p_j\) 可以互换,发现其可以互换当且仅当对于所有 \(x \neq i\) 且 \(x \neq j\),均有 \(\left[\gcd\left(i, x\right) = 1\right] = \left[\gcd\left(j, x\right) = 1\right]\)。可以发现若 \(i, j\) 的质因子集合相同,那么一定可以交换。
设 \(w_{x}\) 表示 \(x\) 的本质不同质因子的乘积(例如 \(w_{8} = 2, w_{24} = 6\)),\(f_{k} = \sum\limits_{1 \le i \le n}\left[w_i = k\right]\)。那么可以发现至此一共有 \(\prod f_i!\) 种交换方案。
考虑质因子集合不同的情况是否可以置换,发现若存在质数 \(p_1, p_2\),满足 \(\forall i, \left[i \times p_1 \le n\right] = \left[i \times p_2 \le n\right]\),那么将所有的 \(k \times p_1\) 和 \(k \times k_2\) 调换位置后排列也一定合法。上述条件可以简化为 \(\left\lfloor\dfrac{n}{p_1}\right\rfloor = \left\lfloor\dfrac{n}{p_2}\right\rfloor\)。
设 \(g_k = \sum\limits_{1 \le i \le n} \left[\left\lfloor\dfrac{n}{i}\right\rfloor = k\right]\)。那么综上我们可以得出一共有 \(\prod f_i! \times \prod g_i!\) 种方案。
接下来考虑存在 \(p_i != 0\) 的情况。可以发现 \(p_i\) 被指定,那么其所属的质因子集合会减少一个可用于交换的元素,另外其还有可能直接指定两个质数间的交换关系。
对于前者的处理是平凡的,将用于统计质因子集合对应元素数量的桶的值减一即可。
对于后者的处理,首先我们需要找出可能会被交换的质因子。根据整除分块的结论,我们可以得出若存在 \(i \neq j\),使得 \(\left\lfloor\dfrac{n}{i}\right\rfloor = \left\lfloor\dfrac{n}{j}\right\rfloor\),那么一定有 \(i, j \ge \sqrt{n}\),而大于 \(\sqrt{n}\) 的质因子每个数最多可能有一个,因此我们直接提取 \(i\) 和 \(p_i\) 的最大质因子进行判断即可,不妨设为 \(x, y\)。若其两者的质数集去除 \(x, y\) 后不同,那么无解。
首先我们需要判断是否满足 \(\left\lfloor\dfrac{n}{x}\right\rfloor = \left\lfloor\dfrac{n}{y}\right\rfloor\),若不满足那么无解。
接下来需要判断 \(x, y\) 是否已经被指定了交换关系。需要注意这里的交换关系是单向的,即 \(x \rightarrow y\),可以理解为将下标中的所有 \(x\) 质因子替换为 $。因为此类交换关系最终一定会形成若干个环,但是环长并不一定为 \(2\)。所以我们需要对交换关系形成的有向图和其反向图存储下来并加以判断。若冲突则无解,否则其对应的 \(g\) 值也需要相应减少 \(1\)。
总复杂度为 \(\mathcal{O}(n \log n)\),瓶颈在于分解质因子。
Code
#include <bits/stdc++.h>
typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;
namespace MODINT {
constexpr valueType MOD = 1e9 + 7;
#define ModOperSafeModOption false
template<typename T1, typename T2, typename T3 = valueType>
void Inc(T1 &a, T2 b, const T3 &mod = MOD) {
a = a + b;
if (a >= mod)
a -= mod;
}
template<typename T1, typename T2, typename T3 = valueType>
void Dec(T1 &a, T2 b, const T3 &mod = MOD) {
a = a - b;
if (a < 0)
a += mod;
}
template<typename T1, typename T2, typename T3 = valueType>
T1 sum(T1 a, T2 b, const T3 &mod = MOD) {
return a + b >= mod ? a + b - mod : a + b;
}
template<typename T1, typename T2, typename T3 = valueType>
T1 sub(T1 a, T2 b, const T3 &mod = MOD) {
return a - b < 0 ? a - b + mod : a - b;
}
template<typename T1, typename T2, typename T3 = valueType>
T1 mul(T1 a, T2 b, const T3 &mod = MOD) {
return (long long) a * b % mod;
}
template<typename T1, typename T2, typename T3 = valueType>
void Mul(T1 &a, T2 b, const T3 &mod = MOD) {
a = (long long) a * b % mod;
}
template<typename T1, typename T2, typename T3 = valueType>
T1 pow(T1 a, T2 b, const T3 &mod = MOD) {
T1 result = 1;
while (b > 0) {
if (b & 1)
Mul(result, a, mod);
Mul(a, a, mod);
b = b >> 1;
}
return result;
}
}// namespace MODINT
using namespace MODINT;
class LineSieve {
public:
typedef ValueVector container;
private:
valueType size;
container minFactorList;
container primeList;
public:
explicit LineSieve(valueType _size_) : size(_size_), minFactorList(_size_ + 1) {
for (valueType i = 2; i <= size; ++i) {
if (minFactorList[i] == 0) {
primeList.push_back(i);
minFactorList[i] = i;
}
for (auto const &iter : primeList) {
valueType const t = i * iter;
if (t > size)
break;
minFactorList[t] = iter;
if (i % iter == 0)
break;
}
}
}
valueType lpf(valueType x) const {
if (x > size)
throw std::range_error("Larger than Size.");
if (x < 1)
throw std::range_error("Too small.");
return minFactorList[x];
}
bool isPrime(valueType x) const {
if (x > size)
throw std::range_error("Larger than Size.");
if (x < 1)
throw std::range_error("Too small.");
return minFactorList[x] == x;
}
const container &getPrime() const {
return primeList;
}
};
void failed() {
std::cout << 0 << std::endl;
std::exit(0);
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
valueType N;
std::cin >> N;
LineSieve const sieve(N);
ValueVector P(N + 1, 0);
for (valueType i = 1; i <= N; ++i)
std::cin >> P[i];
ValueVector primeSetMul(N + 1, 1), primeSetCount(N + 1, 0);
ValueMatrix primeSet(N + 1);
for (valueType i = 2; i <= N; ++i) {
valueType x = i;
while (x > 1) {
valueType const p = sieve.lpf(x);
primeSet[i].emplace_back(p);
primeSetMul[i] *= p;
while (x % p == 0)
x /= p;
}
++primeSetCount[primeSetMul[i]];
}
primeSet[1].emplace_back(1);
++primeSetCount[1];
ValueVector divide(N + 1, 0), divideCount(N + 1, 0);
for (auto const &p : sieve.getPrime()) {
divide[p] = N / p;
++divideCount[divide[p]];
}
divide[1] = 1;
++divideCount[1];
ValueVector index2value(N + 1, 0), value2index(N + 1, 0);
for (valueType i = 1; i <= N; ++i) {
if (P[i] == 0)
continue;
if (primeSet[P[i]].size() != primeSet[i].size())
failed();
for (valueType j = 0; j + 1 < primeSet[P[i]].size(); ++j) {
if (primeSet[P[i]][j] != primeSet[i][j])
failed();
}
valueType const indexPrime = primeSet[i].back(), valuePrime = primeSet[P[i]].back();
if (divide[valuePrime] != divide[indexPrime])
failed();
if (index2value[indexPrime] != 0 && index2value[indexPrime] != valuePrime)
failed();
if (value2index[valuePrime] != 0 && value2index[valuePrime] != indexPrime)
failed();
if (index2value[indexPrime] == 0 && value2index[valuePrime] == 0)
--divideCount[divide[valuePrime]];
index2value[indexPrime] = valuePrime;
value2index[valuePrime] = indexPrime;
--primeSetCount[primeSetMul[P[i]]];
}
ValueVector Fact(N + 1, 0);
Fact[0] = 1;
for (valueType i = 1; i <= N; ++i)
Fact[i] = mul(Fact[i - 1], i);
valueType ans = 1;
for (valueType i = 1; i <= N; ++i)
Mul(ans, Fact[primeSetCount[i]]);
for (valueType i = 1; i <= N; ++i)
Mul(ans, Fact[divideCount[i]]);
std::cout << ans << std::endl;
return 0;
}