【思路要点】
- 考虑 k = 0 k=0 k=0 的情况,当且仅当 A A A 为单位置换,步数为 0 0 0 ,否则,当且仅当 A A A 中的置换环长度均不超过 2 2 2 ,步数为 1 1 1 ,这两种情况的方案数均为 1 1 1 ,以下讨论排除了这两种情况。
- 考虑一个环的情况,不难构造出一种步数为 2 2 2 的解,因此步数应当为 2 2 2 ,不失一般性地,我们认为需要在两次操作内将 1 , 2 , … , N 1,2,\dots,N 1,2,…,N 变为 2 , 3 , … , N , 1 2,3,\dots,N,1 2,3,…,N,1 。
将排列如下对齐,并写下中间步骤的第一个位置。
1 , 2 , 3 , … , N x , 0 , 0 , … , 0 2 , 3 , 4 , … , 1 1,2,3,\dots,N\\x,0,0,\dots,0\\2,3,4,\dots,1 1,2,3,…,Nx,0,0,…,02,3,4,…,1
可以发现,若 x ≠ 1 x\ne1 x̸=1 ,则可以填出中间步骤的第 x x x 个位置为 1 1 1 ,若 x ≠ 2 x\ne2 x̸=2 ,则同样可以填出中间步骤的某个位置为 2 2 2 ,在此过程中,当填入 a ( a < N ) a\ (a<N) a (a<N) 个数时,中间步骤至少 a + 1 a+1 a+1 个位置是确定的,且不会出现一个元素需要同时出现在两处的情况,因此一个环的情况的方案由 x x x 唯一确定,为 N N N 。- 对于两个长度 N N N 相等的环,以 N = 3 N=3 N=3 为例,考虑将其如下对齐:
1 , 2 , 3 , 4 , 5 , 6 x , 0 , 0 , 0 , 0 , 0 2 , 3 , 1 , 5 , 6 , 4 1,2,3,4,5,6\\x,0,0,0,0,0\\2,3,1,5,6,4 1,2,3,4,5,6x,0,0,0,0,02,3,1,5,6,4
类似地,我们可以证明当填入的 x x x 在第二个环上,方案唯一确定。因此其方案数为 N 2 + N N^2+N N2+N 。同时,这也表明了不存在三个环之间的互动。- 那么,对于 i i i 个长度为 N N N 的环,记方案数为 d p i dp_i dpi ,我们便可以用动态规划来解决
d p i = N × d p i − 1 + N × ( i − 1 ) × d p i − 2 dp_i=N\times dp_{i-1}+N\times(i-1)\times dp_{i-2} dpi=N×dpi−1+N×(i−1)×dpi−2- 对于长度不相等的环,用与上文类似的做法可以证明不存在 2 2 2 步以内的交换方式,因此各个长度的环相互独立,可以分别 d p dp dp 。
- 对于 k ≠ 0 k\ne0 k̸=0 的情况,图中给出的就是若干个环和链,枚举链穿成环的方式即可。
- 时间复杂度 O ( N L o g V + k × b e l l ( k ) ) O(NLogV+k\times bell(k)) O(NLogV+k×bell(k)) ,以下代码为实现方便,其复杂度为 O ( N L o g 2 V + k × b e l l ( k ) ) O(NLog^2V+k\times bell(k)) O(NLog2V+k×bell(k)) 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 2e5 + 5; const int P = 1e9 + 7; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int power(int x, int y) { if (y == 0) return 1; int tmp = power(x, y / 2); if (y % 2 == 0) return 1ll * tmp * tmp % P; else return 1ll * tmp * tmp % P * x % P; } void update(int &x, int y) { x += y; if (x >= P) x -= P; } bool vis[MAXN], ind[MAXN]; int n, m, k, cur, ansf, ansg, sum[MAXN][2]; int a[MAXN], len[MAXN], cnt[MAXN], fac[MAXN]; vector <int> dp[MAXN], inv[MAXN]; void work(int pos, int tot) { if (pos > k) { int ways = 1, now = cur; for (int i = 1; i <= tot; i++) { ways = ways * fac[sum[i][0] - 1]; now = 1ll * now * inv[sum[i][1]][cnt[sum[i][1]]] % P; cnt[sum[i][1]]++; now = 1ll * now * dp[sum[i][1]][cnt[sum[i][1]]] % P; } if (cnt[1] == n) update(ansg, ways); else if (cnt[1] + 2 * cnt[2] == n) { ansf += ways; update(ansg, ways); } else { ansf += 2 * ways; update(ansg, 1ll * ways * now % P); } for (int i = 1; i <= tot; i++) cnt[sum[i][1]]--; return; } for (int i = 1; i <= tot; i++) { sum[i][0] += 1; sum[i][1] += len[pos]; work(pos + 1, tot); sum[i][0] -= 1; sum[i][1] -= len[pos]; } sum[tot + 1][0] += 1; sum[tot + 1][1] += len[pos]; work(pos + 1, tot + 1); sum[tot + 1][0] -= 1; sum[tot + 1][1] -= len[pos]; } int main() { freopen("sort.in", "r", stdin); freopen("sort.out", "w", stdout); read(n), read(k), fac[0] = 1; for (int i = 1; i <= k; i++) fac[i] = 1ll * fac[i - 1] * i % P; for (int i = 1; i <= n; i++) { read(a[i]); ind[a[i]] = true; } for (int i = 1; i <= n; i++) { dp[i].resize(n / i + 1); inv[i].resize(n / i + 1); dp[i][0] = inv[i][0] = 1; for (int j = 1; j <= n / i; j++) { dp[i][j] = 1ll * i * dp[i][j - 1] % P; if (j >= 2) update(dp[i][j], 1ll * i * (j - 1) % P * dp[i][j - 2] % P); inv[i][j] = power(dp[i][j], P - 2); assert(dp[i][j] != 0); } } for (int i = 1; i <= n; i++) if (!ind[i]) { int pos = i, pts = 0; while (pos != 0) { vis[pos] = true; pts++, pos = a[pos]; } len[++m] = pts; } for (int i = 1; i <= n; i++) if (!vis[i]) { int pos = i, pts = 0; while (!vis[pos]) { vis[pos] = true; pts++, pos = a[pos]; } cnt[pts]++; } cur = 1; for (int i = 1; i <= n; i++) cur = 1ll * cur * dp[i][cnt[i]] % P; work(1, 0); writeln(ansf); writeln(ansg); return 0; }