dp好题!
首先我们可以求出所有字母都在一侧的方案数dp[n/2];
假设左半部分为集合S,右半部分为T,那么假设选出的a,b已经同属一个集合时
S中可以乱放,T中也可以乱放
那么S中乱放的方案数有:(n/2)!/(num[i]!*num[j]!*.....) (i...j属于S); 1
同样T中乱放的方案数有:(n/2)!/(num[i]!*num[j]!*.....) (i...j属于T); 2
那么总的方案数就是(1式*2式)为:((n/2)!*(n/2)!)/(num[1]!*num[2]!*....*num[n]!) 3
3式我们可以先预处理出来为now
那么最后,a,b在同侧时的答案就是dp[a][b]*now
那么考虑dp[a][b]怎么预处理呢(有一侧选就是另一侧不选,这里我们考虑不选的情况)
我们采用退背包
我们先考虑f[i]为无限制选放满i的方案数(1<=i<=n/2)
g[i]表示不选a放满g[i]的方案数
当i<num[a]时 g[i]=f[i]
当i>=num[a]时 g[i]=f[i]-g[i-num[a]];
h[i]表示不选b放满h[i]的方案数 (在上面的基础上再退一层,表示a,b都不选)
当i<num[b]时 h[i]=f[i]
当i>=num[b]时 h[i]=g[i]-g[i-num[b]];
最后每次询问的答案就是:dp[a][b]*now了
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <map>
using namespace std;
const int mo=1e9+7;
const int maxn=100010;
int n,cnt,v[maxn],q,m;
long long p[maxn],inv[maxn],now,num[55],dp[55][55];
long long f[maxn>>1],g[maxn>>1],h[maxn>>1];
char s[maxn];
map<char,int>lc;
long long po(long long a,int b)
{
if (b==0) return 1ll;
if (b==1) return a%mo;
long long c=po(a,b/2);
if (b&1) return c*c%mo*a%mo;else return c*c%mo;
}
int main()
{
scanf("%s%d",s+1,&q); n=strlen(s+1); p[0]=1ll; m=n/2;
for (int i=1;i<=100000;i++) p[i]=1ll*p[i-1]*i%mo;
inv[100000]=po(p[100000],mo-2);
for (int i=99999;i>=0;i--) inv[i]=1ll*inv[i+1]*(i+1)%mo;
for (char i='A';i<='Z';i++) lc[i]=++cnt;
for (char i='a';i<='z';i++) lc[i]=++cnt;
for (int i=1;i<=n;i++) v[i]=lc[s[i]],num[v[i]]++;
now=p[n/2]%mo*p[n/2]%mo;
for (int i=1;i<=cnt;i++) now=now*inv[num[i]]%mo;
f[0]=1;
for (int j=1;j<=cnt;j++)
if (num[j])
{
for (int i=m;i>=num[j];i--)
if (f[i-num[j]]) f[i]=(f[i]+f[i-num[j]])%mo;
}
for (int i=1;i<=cnt;i++)
if (num[i])
{
dp[i][i]=f[m];
for (int j=0;j<num[i];j++) g[j]=f[j];
for (int j=num[i];j<=m;j++) g[j]=(f[j]-g[j-num[i]]+mo)%mo;
for (int j=i+1;j<=cnt;j++)
if (num[j])
{
for (int k=0;k<num[j];k++) h[k]=g[k];
for (int k=num[j];k<=m;k++) h[k]=(g[k]-h[k-num[j]]+mo)%mo;
dp[i][j]=dp[j][i]=2ll*h[m]%mo;
}
}
while (q--)
{
int xx,yy; scanf("%d%d",&xx,&yy); int aa=v[xx],bb=v[yy];
printf("%lld\n",now*dp[aa][bb]%mo);
}
return 0;
}