看到没人写B的题解,我来写一下吧...场上要是提前几分钟写对就能a5题了呜呜
前置知识:多重集的全排列,快速幂求逆元,阶乘
题目大意:
小红要破解密码,密码是一串0~9的数字,已知每个数字出现的次数,小红每次尝试以后密码都会重置,每个数字出现的次数不变。小红想知道自己尝试次数的期望是多少?
题目分析
- 其实个人不太懂这里期望具体的含义,还想着去oiwiki现学来着,但是转念一想,其实就是求尝试次数,(要尝试多少次才能够成功的意思)。于是我们需要去求这些数字总共能够组成多少种搭配,然后成立密码的有多少种,相除就可以了。(这里相除还有小坑,后面会提到)
- 然后就是一个多重集全排列的公式:
![]()
- 于是就比较明了了,其实就是去预处理一个阶乘,观察数据范围,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取模
}
- 随后套用多重集全排列的那个公式,用一个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;
}
- 随后相除,即为答案,应注意使用逆元:关于逆元的题目也可以看看我的这篇博客,相信会加深理解:逆元题目例子 ,不使用逆元就会像我这样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;
}
京公网安备 11010502036488号