看到没人写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; }