• 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;
    }
    
  • 相关阅读:
    java反射机制2
    Java反射机制
    EKT Java企业级关键技术强化 Enterprise Edition
    idea导入tomcat后解决控制台中文乱码问题(day12)
    大对象数据LOB的应用 (day10)
    Java关键技术强化:JDK5.0新特性 (day08)
    Java关键技术强化:JDK5.0新特性1 (day07)
    Java高效特性:反射(二)之获取Class对象的四种方式及其区别 (day06)
    Java关键技术强化:类加载器 (day05)
    Java高级特效:反射 (day04)
  • 原文地址:https://www.cnblogs.com/xiaoziyao/p/16075958.html
Copyright © 2020-2023  润新知