CodeForces 1863G Swaps

洛谷传送门

CF 传送门

看到 \(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;
}

posted @ 2023-09-17 19:46  zltzlt  阅读(23)  评论(0编辑  收藏  举报