ZOJ 3841 Cards (组合数+乘法逆元)

题目地址: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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值