概率加密
众所周知,小葱同学擅长计算,尤其擅长计算组合数,但这个题和组合数没什么关系。
小葱同学为了庆祝题目套数突破150,小葱同学自创了概率加密算法。现在小葱同学有一个N位的密码,密码的每一位都是1 − N中的一个数。现在小葱自创的随机加密算法会给你N个数xi,代表每次进行加密的时候,所有为i的为都会变成xi,而当N位的密码全部变成和它一开始N位一样之后,小葱同学的加密算法也就停止了。所以,给定这个密码,然后求需要进行多少次加密是一个非常困难的问题。为了简化这个问题,现在小葱假定我们并不知道输入的密码,输入的密码是一个随机的密码,即总共MN种可能性每种可能性的概率为M-N 。小葱同学想要知道,在这种情况下,这个随机密码期望需要多少次能够完成加密操作。
【输入格式】
一行两个数N,M。
接下来一行M个数代表Xi 。
【输出格式】
输出一行一个整数,代表期望的次数乘以MN 模109 + 7的结果。
【样例输入 1】
2 2
1 2
【样例输出 1】
4
【样例输入 2】
2 2
2 1
【样例输出 2】
8
【数据规模与约定】
对于40%的数据,N,M ≤ 5。
70%的数据,N,M ≤ 20。
对于100%的数据,1 ≤ N,M ≤ 100,1 ≤ xi ≤ M,保证所有xi互不相同。
对于单独的一个数i,经过k次重新变为i,保证所有的k乘积小于100。
#include <iostream> #include <cstring> #include <cstdio> #include <cmath> #include <algorithm> #include <map> #define INF 1e9 + 7 #define MAXN 2000 #define int long long #define MOD 1000000007 using namespace std; inline int read() { int x = 0, f = 1; char c = getchar(); while (c > '9' || c < '0') {if (c == '-') f = -1; c = getchar();} while (c >= '0' && c <= '9') {x = x * 10 + (c ^ 48); c = getchar();} return f * x; } inline int max(int a, int b){return a > b ? a : b;} inline int min(int a, int b){return a < b ? a : b;} int n, m, x[MAXN], num[MAXN]; inline int check(int s) { int sum = 1, k = s; k = x[k]; while (k != s){sum++; k = x[k];} return sum; } inline int gcd(int x, int y){return y == 0 ? x : gcd(y, x % y);} inline int lcm(int x, int y){return x * y / gcd(x, y);} int f[105][105][MAXN], vis[MAXN], cnt;//设f[i][j][k]为当前为i位,i位上为j这个数,与前面的数的lcm为k时的方案数,vis储存有多少个lcm map<int, int> VIS; void find(int now, int sum) { if (now == n + 1) return; for (int i = 1; i <= m; i++) { int k = lcm(sum, num[i]); if (!VIS[k]) { vis[++cnt] = k; VIS[k] = 1; find(now + 1, k); } } } inline int step_2() { find(1, 1); for (int i = 1; i <= m; i++) f[1][i][num[i]] = 1; for (int i = 2; i <= n; i++) for (int j = 1; j <= m; j++) for (int k = 1; k <= cnt; k++) for (int N = 1; N <= cnt; N++) if (lcm(vis[N], num[j]) == vis[k]) for (int M = 1; M <= m; M++) f[i][j][k] += f[i - 1][M][N], f[i][j][k] %= MOD; int ans = 0; for (int i = 1; i <= m; i++) for (int j = 1; j <= cnt; j++) ans += f[n][i][j] * vis[j], ans %= MOD; cout << ans << endl; return 0; } signed main() { freopen("prob.in", "r", stdin); freopen("prob.out", "w", stdout); n = read(), m = read(); for (int i = 1; i <= m; i++) x[i] = read(); for (int i = 1; i <= m; i++) num[i] = check(i); return step_2(); return 0; }