一、题目
题目描述
有大小为 的一张字符表,从中随机取出 个字符组成一个字符串,问这个字符串中的子串有多少个从新排列之后能变成回文串,求子串的期望,最后答案要乘上 ,对 取模。
数据范围
二、解法
0x01 dp
其实原问题要求的是所有子串的个数,我们考虑每种子串的贡献。
有一个
不难想到,设
为选了
个字符,偶数相消,剩下了
个字符的总方案,转移如下:
这个转移就是讨论新加入的字符会造成什么影响,花
跑完这个
后,枚举
,考虑
这种子类的贡献,就是它的个数
可能在大串中的起始位置
剩下的字符随便选。
有一个性质,就是 的 同奇同偶,可以利用这一点来卡常qwq。
#include <cstdio>
#include <cstring>
const int MOD = 1e9+7;
const int MAXN = 2005;
int read()
{
int num=0,flag=1;char c;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9')num=(num<<3)+(num<<1)+(c^48),c=getchar();
return num*flag;
}
int T,n,m,ans,pw[MAXN],dp[MAXN][MAXN];
int main()
{
T=read();
while(T--)
{
n=read();m=read();
pw[0]=1;ans=0;
for(int i=1;i<=n;i++)
pw[i]=1ll*pw[i-1]*m%MOD;
dp[0][0]=1;
for(int i=1;i<=n;i++)
{
int j=0;
if(i&1) j=1;
for(;j<=i;j+=2)
{
if(j>=1) dp[i][j]=(1ll*dp[i-1][j-1]*(m-j+1)+1ll*dp[i-1][j+1]*(j+1))%MOD;
else dp[i][j]=1ll*dp[i-1][j+1]*(j+1)%MOD;
}
}
for(int i=1;i<=n;i++)
ans=(ans+1ll*dp[i][i&1]*(n-i+1)%MOD*pw[n-i]%MOD)%MOD;
printf("%d\n",ans);
}
}
0x02 母函数
可以分类讨论,先考虑子串长度为偶数的情况,那么每个字符只能选取偶数,闭形式可以这样表示:
我们对其二项式展开:
我们先枚举子串长度
(
为偶),可以这样算它前面的系数:
如果长度为奇,那么我们就强制一个字符为奇,它的闭形式是
,我们单独加入这个字符,可以这么算:
算出上式之后就可以用和
一样的算贡献方法,时间复杂度
,但是常数会比
大。
#include <cstdio>
#define int long long
const int MOD = 1e9+7;
const int MAXN = 2005;
int read()
{
int num=0,flag=1;char c;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9')num=(num<<3)+(num<<1)+(c^48),c=getchar();
return num*flag;
}
int T,n,m,ans,tmp,pw[MAXN],fac[MAXN],inv[MAXN];
void init(int n)
{
fac[0]=inv[0]=inv[1]=1;
for(int i=2;i<=n;i++) inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD;
for(int i=1;i<=n;i++) inv[i]=inv[i]*inv[i-1]%MOD;
for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%MOD;
}
int C(int n,int m)
{
return fac[n]*inv[m]%MOD*inv[n-m]%MOD;
}
int qkpow(int a,int b)
{
int res=1;
while(b>0)
{
if(b&1) res=res*a%MOD;
a=a*a%MOD;
b>>=1;
}
return res;
}
signed main()
{
init(2000);
T=read();
while(T--)
{
n=read();m=read();
ans=0;
pw[0]=1;
for(int i=1;i<=n;i++)
pw[i]=1ll*pw[i-1]*m%MOD;
for(int i=1;i<=n;i++)
{
tmp=0;
if((i&1)==0)
{
for(int j=0;j<=m;j++)
tmp=(tmp+C(m,j)*qkpow(m-2*j,i)%MOD)%MOD;
}
else
{
for(int j=0;j<m;j++)
tmp=(tmp+C(m-1,j)*(qkpow(m-2*j,i)-qkpow(m-2*j-2,i))%MOD)%MOD;
tmp=tmp*m%MOD;
}
tmp=tmp*qkpow(qkpow(2,m),MOD-2)%MOD;
ans=(ans+tmp*(n-i+1)%MOD*pw[n-i]%MOD)%MOD;
}
printf("%lld\n",(ans%MOD+MOD)%MOD);
}
}