题解 P5684 【非回文串】
NaCly_Fish · · 题解
很简单的数数入门题,比 CSP-S Day2 T1 良心多了(
注意到每个字符都是有标号的,所以非回文串数等于
记第
为了下面表述方便,默认所有
由于回文串是左右对称的,所以只考虑确定一边,另一边的每个字母都可以在对应位置随便排列(当然左右可以互换),也就是
所以对于左边的每一种排列,贡献就是每个
那么现在只需要算出:每种元素有
如果没有重复,排列数当然是
不嫌麻烦也可以用 EGF 来推出这个结果,不用动脑子(
所以最终答案就是
不过这是
对于有一个是奇数的时候,设为
时间复杂度
#include<cstdio>
#include<algorithm>
#include<cstring>
#define ll long long
#define N 2003
#define reg register
#define p 1000000007
using namespace std;
inline int power(int a,int t){
int res = 1;
while(t){
if(t&1) res = (ll)res*a%p;
a = (ll)a*a%p;
t >>= 1;
}
return res;
}
int n,ans,dec,odd;
char a[N];
int fac[N],ifac[N],cnt[26];
int main(){
scanf("%d",&n);
scanf("%s",a+1);
ifac[0] = ifac[1] = fac[0] = fac[1] = 1;
for(reg int i=2;i<=n;++i) ifac[i] = fac[i] = (ll)fac[i-1]*i%p;
ifac[n] = power(fac[n],p-2);
for(reg int i=n-1;i>1;--i) ifac[i] = (ll)ifac[i+1]*(i+1)%p;
for(reg int i=1;i<=n;++i) ++cnt[a[i]-'a'];
for(reg int i=0;i<26;++i) odd += cnt[i]&1;
if(odd>1){
printf("%d",fac[n]);
return 0;
}
odd = 1;
for(reg int i=0;i<26;++i)
if(cnt[i]&1) odd = cnt[i];
dec = (ll)fac[n>>1]*odd%p;
for(reg int i=0;i<26;++i) cnt[i] >>= 1;
for(reg int i=0;i<26;++i) dec = (ll)dec*fac[cnt[i]<<1]%p*ifac[cnt[i]]%p;
ans = (fac[n]-dec+p)%p;
printf("%d",ans);
return 0;
}