原题链接

看到没人写B的题解,我来写一下吧...场上要是提前几分钟写对就能a5题了呜呜

前置知识:多重集的全排列,快速幂求逆元,阶乘

 题目大意:

 小红要破解密码,密码是一串0~9的数字,已知每个数字出现的次数,小红每次尝试以后密码都会重置,每个数字出现的次数不变。小红想知道自己尝试次数的期望是多少?

图片说明

题目分析

  1. 其实个人不太懂这里期望具体的含义,还想着去oiwiki现学来着,但是转念一想,其实就是求尝试次数,(要尝试多少次才能够成功的意思)。于是我们需要去求这些数字总共能够组成多少种搭配,然后成立密码的有多少种,相除就可以了。(这里相除还有小坑,后面会提到)
  1. 然后就是一个多重集全排列的公式:图片说明
  1. 于是就比较明了了,其实就是去预处理一个阶乘,观察数据范围,0<=a<=1e6,而一共有10个数,所以总共需要一个1e7大小的数组去存阶乘的值。

代码:

    //预处理一波阶乘
    f[0] = 1;//0的阶乘为1
    f[1] = 1;//1的阶乘为1
    for(int i = 2;i<=1e7+10;i++)
    {
        f[i] = (f[i-1]*i)%mod;//注意对mod取模
    }
  1. 随后套用多重集全排列的那个公式,用一个long long 数sum1去保存公式上半部分的和,随后通过f[num1]得到需要的阶乘,也就是图片说明 ,随后通过long long 数 sum2去保存下半部分,即图片说明
    代码:
ll num1 = 0;
    for(int i = 0;i<10;i++)
        num1+=a[i];//得到所有数的个数
    ll num2 = 1;
    for(int i = 0;i<10;i++)
    {
        if(a[i]!=0)
            num2 = (num2*f[a[i]])%mod;
    }
  1. 随后相除,即为答案,应注意使用逆元:关于逆元的题目也可以看看我的这篇博客,相信会加深理解:逆元题目例子 ,不使用逆元就会像我这样wa掉:图片说明

WA code:

    cout<<f[num1]/num2;

AC code:

    cout<<cout<<f[num1]*qmi(num2,mod-2,mod)%mod;
//当然要写一个快速幂的板子求逆元啦

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e7+10;//这里必须开到1e7

const ll mod = 1e9+7;//不能用int
int a[10];
ll f[N];

ll qmi(ll a,ll b,ll mod)
{//快速幂
    //qmi(a,mod-2,mod)
    ll ans = 1;
    while(b)
    {
        if(b&1) ans = ans*a%mod;
        b>>=1;
        a = a*a%mod;
    }
    return ans;
}
int n;
int main()
{
    for(int i = 0;i<10;i++)
        cin>>a[i];
    ll ans = 0;
    f[0] = 1;
    f[1] = 1;
    for(int i = 2;i<=1e7+10;i++)
    {
        f[i] = (f[i-1]*i)%mod;
    }
    ll num1 = 0;
    for(int i = 0;i<10;i++)
        num1+=a[i];
    ll num2 = 1;
    for(int i = 0;i<10;i++)
    {
        if(a[i]!=0)
            num2 = (num2*f[a[i]])%mod;
    }
    cout<<f[num1]*qmi(num2,mod-2,mod)%mod;
    return 0;
}