概率加密

众所周知,小葱同学擅长计算,尤其擅长计算组合数,但这个题和组合数没什么关系。


小葱同学为了庆祝题目套数突破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;
}
View Code

 

posted @ 2020-05-30 10:26  dead_gun  阅读(295)  评论(0编辑  收藏  举报