[HDU 5362]Just A String

一、题目

题目描述

有大小为 m m 的一张字符表,从中随机取出 n n 个字符组成一个字符串,问这个字符串中的子串有多少个从新排列之后能变成回文串,求子串的期望,最后答案要乘上 m n m^n ,对 1 e 9 + 7 1e9+7 取模。

数据范围

1 n , m 2000 1\leq n,m\leq 2000

二、解法

0x01 dp

其实原问题要求的是所有子串的个数,我们考虑每种子串的贡献。

有一个 d p dp 不难想到,设 d p [ i ] [ j ] dp[i][j] 为选了 i i 个字符,偶数相消,剩下了 j j 个字符的总方案,转移如下:
d p [ i ] [ j ] = d p [ i 1 ] [ j + 1 ] × ( j + 1 ) + d p [ i 1 ] [ j 1 ] × ( m j + 1 ) dp[i][j]=dp[i-1][j+1]\times (j+1)+dp[i-1][j-1]\times (m-j+1) 这个转移就是讨论新加入的字符会造成什么影响,花 O ( n 2 ) O(n^2) 跑完这个 d p dp 后,枚举 i i ,考虑 d p [ i ] [ i & 1 ] dp[i][i\&1] 这种子类的贡献,就是它的个数 × \times 可能在大串中的起始位置 × \times 剩下的字符随便选。

有一个性质,就是 d p dp i , j i,j 同奇同偶,可以利用这一点来卡常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 母函数

可以分类讨论,先考虑子串长度为偶数的情况,那么每个字符只能选取偶数,闭形式可以这样表示:
e x + e x 2 \frac{e^x+e^{-x}}{2} 我们对其二项式展开:
2 m i = 0 m C m i × e ( m 2 i ) x 2^{-m}\sum_{i=0}^m C_{m}^i\times e^{(m-2i)x} 我们先枚举子串长度 i i i i 为偶),可以这样算它前面的系数:
2 m j = 0 m C m j × ( ( m 2 j ) x ) i 2^{-m}\sum_{j=0}^m C_{m}^j\times ((m-2j)x)^i 如果长度为奇,那么我们就强制一个字符为奇,它的闭形式是 e x e x 2 \frac{e^x-e^{-x}}{2} ,我们单独加入这个字符,可以这么算:
m 2 m j = 0 m 1 C m 1 j × ( ( 2 j + 2 m ) i ( 2 j m ) i ) m\cdot 2^{-m}\sum_{j=0}^{m-1} C_{m-1}^j\times ((2j+2-m)^i-(2j-m)^i) 算出上式之后就可以用和 d p dp 一样的算贡献方法,时间复杂度 O ( n m ) O(nm) ,但是常数会比 d p dp 大。

#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);
	}
}
发布了192 篇原创文章 · 获赞 12 · 访问量 3360

猜你喜欢

转载自blog.csdn.net/C202044zxy/article/details/103731723