链接:https://ac.nowcoder.com/acm/contest/33186/I
来源:牛客网
a≡b(mod c)。28≡13(mod 5)gcd(a,n)=1。ax ≡ 1(mod n),则称 x 的最小正整数解为 a 模 n的逆元。结合题目 (a / b)%p
由于是对除法取模,往往容易出现精度问题,不得不用高精除法但是那会加重代码负担,所以我们可以通过使用逆元把除法取余转化为乘法取余。

3. 费马小定理求逆元
费马小定理证明

求逆元(注意区分逆和-1次方)

4. 快速幂
快速幂(Exponentiation by squaring,平方求幂)是一种简单而有效的小算法,它可以以O(logn)的时间复杂度计算乘方。快速幂不仅本身非常常见,而且后续很多算法也都会用到快速幂。
快速幂模板
//x^n
ll qkp(ll x,ll n,ll p)
{
ll ans = 1;
while(n > 0)
{
if(n&1)//指数是奇数
ans = ans*x %p;
n >>= 1;//二进制位置左移 ,指数除以
x = x*x %p;//底数平方
}
return ans;
}
结合题目上文求逆元为 b的p-2次方
题目为多组样例题目,且范围为 1≤T≤1e5,当执行多次求幂肯定会TLE则使用快速幂解决。
关于状态方程dp[i][j]表示手上还有i张单牌,牌堆还有j张牌时还需要摸牌次数的期末值。
每次摸牌会有两种情况但无论哪种情况牌堆的牌数都会减少一张(j-1):

# include
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
int t;
string s;
int sum = 34 * 4 - 13;
ll dp[15][150];//手上有i张,牌堆里还剩j张牌时还需要摸牌的期望次数
ll qkm(ll a,ll n){
ll ans = 1;
while(n > 0){
if(n & 1) ans = ans * a % mod;
n >>= 1;
a = a * a % mod;
}
return ans;
}
void init(){
//为啥从三开始:当牌堆剩2两张牌时一定能胡
//初始化dp[1][j]:防止 i-2为负 的情况。
for(int j=3;j<=sum;j++) dp[1][j] = (1 + (((j - 3) * qkm(j,mod-2) % mod) * dp[1][j-1]) % mod) % mod;
//手上为单牌的情况之后1、3、5、7、9、11、13,1已经初始化过了,所以i从三开始
for(int i=3;i<=13;i+=2){
for(int j=3;j<=sum;j++){
dp[i][j] = ((1 + ((((j - 3 * i) * qkm(j,mod-2) % mod) * dp[i][j-1]) % mod))
+
((((3 * i) * qkm(j,mod-2) % mod) * dp[i-2][j-1]) % mod)) % mod;
}
}
}
int main(){
cin >> t;
//处理所有可能的dp结果
init();
for(int k=1;k<=t;k++){
cin >> s;
map<string,int> mp;//用于统计单牌数
for(int i=0;i<s.size();i+=2){
string res = "";
res += s[i];
res += s[i+1];
mp[res]++;
}
int single = 0;
for(auto it : mp)
//cout << it.first << ":" << it.second << endl;
if(it.second == 1) ++single;
//cout << single << endl;
cout << "Case #" << k << ": " << dp[single][sum] << endl;
}
return 0;
}