逆元在阶乘上的运用

what’s 逆元

原题xiaoxin juju needs help

题意

给一字符串,问用这个字符串里面的字符可以拼成多少个不同的回文串

解析

把回文串分成左右两边,即字母x出现次数除2,求排列C

假设左半边有x个字母a,y个字母b,z个字母c,C==(x+y+z)! / x! / y! / z!

普通的做法有个bug,(x+y+z)!当然可以做,但是%mod后再除x!呢?举个例子,10000/100可以除,但是10000%107再/100就会造成精度上的错误。

用了逆元就完全不用担心了,我们先求出(x+y+z)!,再乘x!的逆元,y!的逆元,z!的逆元,就避免了这种错误。

代码

#include<stdio.h>
#include<string>
#include<iostream>
#define D long long
const D mod=(D)(1e9+7);
using namespace std;

D swift(D a,D b){
    D bas=a;D mul=1;
    while(b){
        if(b&1)mul=mul*bas%mod;
        bas=bas*bas%mod;b>>=1;
    }return mul;
}

D inv_fac[1007];
D fac[1007];

void init(){
    fac[1]=1;
    for(D i=2;i<=1000;i++)fac[i]=fac[i-1]*i%mod;//初始化阶乘 

    inv_fac[1000]=swift(fac[1000],mod-2);//费马小定理求 1000!的逆元 

    for(D i=999;i>=0;i--)  
    inv_fac[i]=(inv_fac[i+1]*(i+1))%mod; //预处理 i!的逆元 
}



void process(){
    string x;
    cin>>x;

    if(x.length()==1){printf("1\n");return ;}

    int nu[27];for(int i=1;i<=26;i++)nu[i]=0; 

    for(int i=0;i<x.length();i++)nu[x[i]-'a'+1]++;

    int tji=0;
    for(int i=1;i<=26;i++)if(nu[i]%2==1)tji++;
    if(tji>1){printf("0\n");return;}
    //因为取一半,所以奇数的那个不用管 

    D ans=fac[x.length()/2];
    for(int i=1;i<=26;i++){
        if(!nu[i]||nu[i]==1)continue;
        ans=ans*inv_fac[nu[i]/2]%mod;
    }
    printf("%lld\n",ans);
}


int main(){
    init();
    int t;
    scanf("%d",&t);
    while(t--){
        process();
    }
} 

/*
3
*/
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值