HDU 5651 组合+逆元

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

题目意思我看了半天没读懂,一直以为是回文子串又没看见substring的单词最后看博客才知道是用给出的字符任意组合。

求不同的回文串个数,显然对于一个长度为奇数的串,我们可以枚举中间位置的元素,然后计算由剩余字符对半分(如果不可以就是零)之后的

排列数就好了,由于有重复字符排列数公式也会不同,偶数串的话直接计算一次就好了。

假设串长度为n,有m种不同元素,每种有ai个,则排列数就是    n!/(a1!*a2!*a3!*......am!)

接着讨论具体计算过程,

对于solve(),就是计算在当前合法状态下,对半分之后的排列总数对mod取余,公式就是

len!/(a1!*a2!*a3!......*am!)%mod,由于阶乘值会很大所以只能用同余公式和逆元进行化简,

==> (1%mod*2%mod*....*len%mod)%mod*(1/a1!)%mod*(1/a2!)*......*(1/am!)%mod   //我们用fac[i]表示阶乘取模,prv[i]表示i!的逆对mod取模

//对于1/x!%mod<==>1*(1/2)%mod*(1/3)%mod*...(1/x)%mod<==> 2-1%mod*3-1%mod......*x-1%mod

==>fac[i]*prv[a1]*prv[a2]*.....*prv[am]%mod;

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define LL long long
 4 LL mod=1e9+7;
 5 LL a[30];
 6 LL fac[1005]={1};
 7 LL inv[1005]={1,1};
 8 LL prv[1005]={1,1};
 9 LL solve(int len)
10 {
11     int i,j,k;
12     LL res=fac[len];
13     for(i=0;i<26;++i)
14     {
15         if(a[i]&1) return 0;
16         a[i]/=2;
17         res=res*prv[a[i]]%mod;
18         a[i]*=2;
19     }
20     return res;
21 }
22 int main()
23 {
24     int t,i,j,n,m;
25     char str[1005];
26     for(LL i=1;i<=1000;++i) fac[i]=fac[i-1]*i%mod;
27     for(i=2;i<=1000;++i) {
28             inv[i]=(mod-mod/i)*inv[mod%i]%mod;
29             prv[i]=prv[i-1]*inv[i]%mod;
30     }
31     cin>>t;
32     while(t--){memset(a,0,sizeof(a));
33        scanf("%s",str);
34        int len=strlen(str);
35        for(i=0;i<len;++i){
36         a[str[i]-'a']++;
37        }LL ans=0;
38        if(len&1){
39         for(i=0;i<26;++i){
40             if(!a[i]) continue;
41             a[i]--;
42             ans=(ans+solve((len-1)/2))%mod;
43             a[i]++;
44         }
45        }
46        else{
47        ans=solve(len/2);
48        }
49        cout<<ans<<endl;
50     }
51     return 0;
52 }

 

posted @ 2017-07-26 17:53  *zzq  阅读(196)  评论(0编辑  收藏  举报