HDU xiaoxin juju needs help (逆元+排列组合)

转载地址:http://blog.csdn.net/saber_acher/article/details/50994112

题目:http://acm.hdu.edu.cn/showproblem.php?pid=5651

题意:给你遗传由若干个小写字母组成的字符串,你可以任意变动互相之间的位子,但不能删除或添加元素,问可以组成多少种不同回文串。思路:很简单的排列组合,太简单我就不把他分到排列组合里去了。先判断是否可以组成回文串,如果有一个以上的奇数个同一的字母,则无论如何都不会组成回文串。剩下的就枚举回文串的一边,所有的元素都取半。所有元素的一半的和的阶乘除于每个元素一半的的阶乘即是答案。这就是问题的所在,除于之后是要取模的。除法取模问题当然是逆元了,谈到逆元当然是费马小定理喽。费马小定理:费马小定理(Fermat Theory)数论中的一个重要定理,其内容为: 假如p是质数,且(a,p)=1,那么 a(p-1)≡1(mod p)。即:假如a是整数,p是质数,且a,p互质(即两者只有一个公约数1),那么a的(p-1)次方除以p的余数恒等于1。

#include<stdio.h>  
#include<string.h>  
const int maxn=1111;  
int num[maxn];  
char s[maxn];  
typedef long long ll;  
const int mod=1e9+7;  
ll f[maxn];  
void init()  
{  
    f[0]=1;  
    f[1]=1;  
    for(int i=2;i<maxn;i++)  
       f[i]=f[i-1]*i%mod;  
}  
ll cal(ll x)  
{  
    ll res=1;  
    int k=mod-2;  
    while(k)  
    {  
        if(k&1)  
        {  
            res*=x;  
            res%=mod;  
        }  
  
        x*=x;  
        x%=mod;  
        k>>=1;  
    }  
    return res;  
}  
int main()  
{  
    int T;  
    init();  
    while(scanf("%d",&T)!=EOF)  
    while(T--)  
    {  
        scanf("%s",s);  
        int n=strlen(s);  
        memset(num,0,sizeof(num));  
        for(int i=0;i<n;i++)  
           num[s[i]-'a']++;  
        int len=0;  
        for(int i=0;i<26;i++)  
        if(num[i]%2)  
        len++;  
        if(len>1)  
        {  
            printf("0\n");  
            continue;  
        }  
        int total=0;  
        for(int i=0;i<26;i++)  
        {  
            num[i]/=2;  
            total+=num[i];  
        }  
        ll res=f[total];  
        for(int i=0;i<26;i++)  
        if(num[i])  
        {  
            res=res*cal(f[num[i]])%mod;  
        }  
        printf("%lld\n",res);  
    }  
    return 0;  
}  



too difficult!!!!

你可能感兴趣的:(HDU xiaoxin juju needs help (逆元+排列组合))