CodeForces 1863G Swaps
看到 \(a_{a_i}\) 和 \(a_i \in [1, n]\),果断连边 \(i \to a_i\),得到内向基环森林。
那么每次相当于把 \(a_i\) 变成自环,连边 \(i \to a_{a_i}\)。
但是每次操作都改变图的形态很不好办,考虑打标记。
每次 \(\operatorname{swap}(a_i, a_{a_i})\),我们就把 \(i \to a_i\) 的边打上标记。那么经过若干次操作后,\(a_i\) 实际上是,不断跳出边,第一条没被打标记的边指向的点。
考虑把最终的 \(a\) 一一对应到对边打标记的方案。每个点至多有一条出边被打标记,设点 \(u\) 的入度为 \(d_u\),方案数为 \(\prod d_u + 1\)。
但是我们发现,对于一个长度为 \(k\) 的环,如果环上有 \(k - 1\) 条边被标记,那整个环就都变成自环了。
有 \(\sum\limits_{u \in \text{cycle}} d_u + 1\) 个方案对应最后环上的点全部变成自环(钦定一个点没有入边被标记或者有一条除环上边之外的入边被标记,或者整个环的边都被标记),减去 \(\sum\limits_{u \in \text{cycle}} d_u\)。因此最后答案为:
\[(\prod\limits_{u \in \text{cycle}} (d_u + 1) - \sum\limits_{u \in \text{cycle}} d_u) \prod\limits_{u \notin \text{cycle}} (d_u + 1)
\]
code
// Problem: G. Swaps
// Contest: Codeforces - Pinely Round 2 (Div. 1 + Div. 2)
// URL: https://codeforces.com/problemset/problem/1863/G
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;
const int maxn = 1000100;
const ll mod = 1000000007;
ll n, a[maxn], ind[maxn], fa[maxn], b[maxn];
vector<int> vc[maxn];
int find(int x) {
return fa[x] == x ? x : fa[x] = find(fa[x]);
}
inline void merge(int x, int y) {
x = find(x);
y = find(y);
if (x != y) {
fa[x] = y;
}
}
void solve() {
scanf("%lld", &n);
for (int i = 1; i <= n; ++i) {
scanf("%lld", &a[i]);
++ind[a[i]];
fa[i] = i;
}
for (int i = 1; i <= n; ++i) {
merge(i, a[i]);
}
for (int i = 1; i <= n; ++i) {
vc[find(i)].pb(i);
}
ll ans = 1;
for (int _ = 1; _ <= n; ++_) {
if (vc[_].empty()) {
continue;
}
int x = vc[_][0];
while (b[x] <= 3) {
++b[x];
x = a[x];
}
ll res = 1;
for (int x : vc[_]) {
if (b[x] >= 3) {
res = res * (ind[x] + 1) % mod;
}
}
for (int x : vc[_]) {
if (b[x] >= 3) {
res = (res - ind[x] + mod) % mod;
}
}
for (int x : vc[_]) {
if (b[x] < 3) {
res = res * (ind[x] + 1) % mod;
}
}
ans = ans * res % mod;
}
printf("%lld\n", ans);
}
int main() {
int T = 1;
// scanf("%d", &T);
while (T--) {
solve();
}
return 0;
}