题目地址:ZOJ 3841
这题很快就写出来了,但是我犯了一个很**的错误,忘了把0的阶乘设为1。。卡了整整两个小时。。。写完题解就去面壁思过。。不要拦我。。
这题的思路是从前往后遍历,对于第i位来说,从比他小的牌开始枚举,枚举的比当前牌小的话,那么后面的放什么都可以,所以求全排列,求全排列的过程中要用到乘法逆元(幸亏CF补题场场不落。。最近从CF补题中学会的。。)。然后枚举到跟当前牌相同的时候,那就再枚举下一位。
代码如下:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <algorithm>
#define INF 0x3f3f3f3f
#define LL long long
const int mod=1e9+7;
using namespace std ;
char s[200];
LL ha[20], b[200], cnt, inv_fac[60], fac[60], tot;
LL qsm(LL x)
{
LL ans=1;
LL m=mod-2;
while(m>0) {
if(m&1) ans=ans*x%mod;
m>>=1;
x=x*x%mod;
}
return ans;
}
void init()
{
int i;
fac[1]=fac[0]=1;
for(i=2; i<=52; i++) {
fac[i]=fac[i-1]*i%mod;
if(i>4) continue ;
inv_fac[i]=qsm(fac[i]);
}
inv_fac[1]=inv_fac[0]=1;
}
LL get(int y)
{
int i;
LL ans=fac[tot-y-1];
for(i=1; i<=13; i++) {
if(ha[i]==0) continue ;
ans=ans*inv_fac[ha[i]]%mod;
}
return ans;
}
int f(char c)
{
if(c=='A') return 1;
else if(c>='2'&&c<='9') return c-'0';
else if(c=='1') return 10;
else if(c=='J') return 11;
else if(c=='Q') return 12;
else if(c=='K') return 13;
}
int main()
{
int len, i, j, x;
LL ans;
init();
while(scanf("%s",s)!=EOF) {
len=strlen(s);
memset(ha,0,sizeof(ha));
cnt=0;
ans=0;
for(i=0; i<len; i++) {
if(s[i]=='0') continue;
b[cnt++]=f(s[i]);
ha[f(s[i])]++;
}
tot=52-cnt;
for(i=1; i<=13; i++) {
ha[i]=4-ha[i];
}
for(i=0; i<cnt; i++) {
for(j=1; j<b[i]; j++) {
if(ha[j]==0) continue ;
ha[j]--;
ans+=get(i);
ans%=mod;
ha[j]++;
}
if(ha[b[i]]==0) break;
ha[b[i]]--;
}
if(i<cnt&&i==tot) ans++;
printf("%lld\n",ans%mod);
}
return 0;
}