Examples

P5417 [CTSC2016]萨菲克斯·阿瑞 解题报告

P5417 [CTSC2016]萨菲克斯·阿瑞 解题报告:

更好的阅读体验

3000 AC。

题意

计数长度为 \(n\),字符集大小为 \(m\),且每个字符出现次数不超过 \(c_i\) 的 SA 数量。

\(1\leqslant n,m\leqslant 500\)

分析

直接对 SA 的定义计数看上去就无从下手,考虑找一个性质转化。

考虑排序后相邻的两个后缀 \(p_i,p_{i+1}\)\(p\) 就是 SA 数组),显然有 \(s_{p_i}\leqslant s_{p_{i+1}}\)。若取等,那么就要求 \(p_i+1\) 对应的后缀在 SA 内的位置比 \(p_{i+1}+1\) 对应的后缀前,否则没有要求。

可以发现上面那个性质也是充分的,即:

\(p_i+1\) 在 SA 中的位置比 \(p_{i+1}+1\) 前,那么 \(s_{p_i}\leqslant s_{p_{i+1}}\),否则 \(s_{p_i}<s_{p_{i+1}}\)

可以将这些限制看成一个不等式链,我们现在想要对于每个合法的不等式链,计数其对应 SA 数量之和。

先考虑一个简单一些的问题,怎么判定一个不等式链合法:

用小于号将链分成若干段,每一段都贪心地填充,拿出最小的字符,能填完这个字符就填完,否则把这一段剩下的位置全部填满,然后丢掉这个字符。合法的条件就是能顺利进行这个过程。

然后考虑怎么计数:

小于号很烦人,于是我们容斥有哪些小于号变成了等于号,计算等于号和小于等于号的不等式链对应的 SA 数量。

手玩观察(?)可得方案数应为:按照没有改变的小于号分段,每一段的长度共同形成的多重组合数。

带上容斥系数,这个的贡献应该是这样的一个形式:(\(m-k\) 就是等号的数量)

\[(-1)^{m-k}{n\choose l_1,l_2,\cdots,l_k} \]

考虑 dp 容斥系数和(这个歌过程类似于不等式链的判定),令 \(f_{r,i,j}\) 表示只塞入 \(1\)\(r\) 的字母,放了 \(i\) 个位置进去,目前的段长度为 \(k\) 的容斥系数和。

转移:

  1. 用字母 \(r\) 填满这一段,然后分段:$$f_{r+1,i+k,0}\leftarrow \frac{1}{(j+k)!}\cdot f_{r,i,j}$$
  2. 把字母 \(r\) 填完:$$f_{r,i+c_r,j+c_r}\leftarrow f_{r,i,j}$$
  3. 钦定一个等号:$$f_{r,i+k,j+k}\leftarrow (-1)\cdot f_{r,i,j}$$

可以发现第二类转移和第三类转移能合并,\(k\) 的范围从 \([1,c_r]\) 改为 \([1,c_r)\) 即可。

两类转移都可以用前缀和优化一下,于是复杂度就是 \(O(n^2m)\) 了。

代码

记得把 \(c=0\) 的字母去掉。

#include<stdio.h>
const int maxn=505,mod=1000000007;
int n,m,M,ans;
int c[maxn],C[maxn],f[maxn][maxn],sum[maxn][maxn],fac[maxn],inv[maxn],nfac[maxn];
int main(){
	scanf("%d%d",&n,&M);
	fac[0]=nfac[0]=1;
	for(int i=1;i<=n;i++)
		fac[i]=1ll*fac[i-1]*i%mod,inv[i]=i==1? 1:(mod-1ll*(mod/i)*inv[mod%i]%mod),nfac[i]=1ll*nfac[i-1]*inv[i]%mod;
	for(int i=1;i<=M;i++){
		scanf("%d",&C[i]);
		if(C[i])
			c[++m]=C[i];
	}
	f[0][0]=1;
	for(int r=1;r<=m;r++){
		for(int i=0;i<=n;i++)
			for(int j=0;j<=i;j++)
				sum[i][j]=((i==0||j==0? 0:sum[i-1][j-1])+f[i][j])%mod,f[i][j]=0;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=i;j++){
				f[i][0]=(f[i][0]+1ll*nfac[j]*(sum[i-1][j-1]-(j<=c[r]? 0:sum[i-c[r]-1][j-c[r]-1])+mod))%mod;
				f[i][j]=(0ll+f[i][j]-(sum[i-1][j-1]-(j<c[r]? 0:sum[i-(c[r]-1)-1][j-(c[r]-1)-1]))+mod)%mod;
			}
		ans=(ans+f[n][0])%mod;
	}
	printf("%d\n",1ll*ans*fac[n]%mod);
	return 0;
}
posted @ 2022-03-30 11:00  xiaoziyao  阅读(107)  评论(0编辑  收藏  举报