CF135E Weak Subsequence (计数问题)

题目传送门

题目大意:对于给定字符集大小k,求有多少个字符串满足它的最长【既是子串又是弱子序列】的长度为w;

 

神仙计数题

打表发现,对于字符串S而言,它的最长【既是子串又是弱子序列】,一定存在一个对应的子串,是S的前缀或者后缀

如果不是前缀或者后缀,那么它一定还可以向两边扩展

这启示我们分类讨论

容易发现,最优的情况一定是除了最后一位以外,其它每个字符都在相同的位置取,最后一位在其他位置取来构成弱子序列。

我们重点考虑最后一位带来的影响,

假设现在我们找到了一个字符串$w$作为子串,在后面接上一段字符串$L$

我们要保证$w$是最长的【既是子串又是弱子序列】才能正确地统计答案

1.$L$中一定存在一个字符和第$w$个字符相等

2.$L$中每个字符都不相等,否则继续向前取还能更长

3.前$L$个字符互不相等,否则从后面开始取能更长

有了这三个条件,我们就可以开始讨论了:

1.$w-L\geq 2$时,把序列分成5块,$[1,L], L+1, [L+2,w-1], w, [w+1,w+L]$

$[1,L]$存在一个字符和第L+1位相等,$[w+1,w+L]$位存在一个字符和第$w$位相等,两种情况取交集,可得答案为:

$k^{w-L-2}((A_{k}^{L})^{2}k^{2}-(A_{k}^{L+1})^{2})$

中间$w-L-2$个随便取,两边$L$个分别从$k$个里取且互不相等,第$L+1$个和第$w$个先假设随便取,再去掉两边都没有相同的情况

2.$w-L=1$时,把序列分成3块,$[1,L], w, [w+1,w+L]$

$[1,L]$位存在一个字符和第$w$位相等,$[w+1,w+L]$位存在一个字符和第$w$位相等,取交集,答案为:

$k(A_{k}^{L})^{2}-A_{k}^{L+1}A_{k-1}^{L}$

比上面的情况还要简单,不解释了

3.$w\leq L$时,把序列分成5块,$[1,w-1], w, [w+1,L], L+1, [L+2,L+w]$

这种情况就比较复杂了,但大体思路不变

首先,中间$[w+1,L]$一共$L-w$个,是从$k$个里随便取的且互不相同,贡献是$A_{k}^{L-w}$

左右两边$w$个都是从$k-(L-w)$里随便取,贡献是$A_{k-(L-w)}^{w}$

两边都没有的情况的贡献呢?

$w, [w+1,L], L+1$都互不相同,贡献是$A_{k}^{L-w+2}$。前后$w-1$个都和中间$[w,L+1]$个互不相同,贡献是$A_{k-(L-w+2)}^{w-1}$

总贡献就是$A_{k}^{L-w}(A_{k-(L-w)}^{w})^{2}-A_{k}^{L-w+2}(A_{k-(L-w+2)}^{w-1})^{2}$

$L$里每个元素互不相同,所以长度最大就是$k$,暴力枚举然后计算即可

 

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #define ll long long 
 5 #define N1 1000010
 6 using namespace std;
 7 const ll p=1000000007;
 8 
 9 ll mul[N1],_mul[N1],inv[N1];
10 int m,K,n;
11 inline ll C(int x,int y)
12 {
13     if(y>x) return 0;
14     return mul[x]*_mul[y]%p*_mul[x-y]%p;
15 }
16 inline ll A(int x,int y)
17 {
18     if(y>x) return 0;
19     return mul[x]*_mul[x-y]%p;
20 }
21 ll qpow(ll x,ll y)
22 {
23     if(y<0) return 0; ll ans=1;
24     for(;y;x=x*x%p,y>>=1) if(y&1) ans=ans*x%p;
25     return ans;
26 }
27 
28 int main()
29 {
30     scanf("%d%d%d",&m,&K,&n);
31     int i,j,L;
32     mul[0]=mul[1]=_mul[0]=_mul[1]=inv[0]=inv[1]=1;
33     for(i=2;i<=K;i++) mul[i]=mul[i-1]*i%p, inv[i]=1ll*(p-p/i)*inv[p%i]%p, _mul[i]=_mul[i-1]*inv[i]%p;
34     ll ans=0;
35     for(L=1;L<=K&&n+L<=m;L++)
36     {
37         if(n-L>=2) (ans+=qpow(K,n-L-2)*(1ll*K*K%p*A(K,L)%p*A(K,L)%p-A(K,L+1)*A(K,L+1)%p+p)%p)%=p;
38         else if(n-L==1) (ans+=(1ll*K*A(K,L)%p*A(K,L)%p-A(K,L+1)*A(K-1,L)%p+p)%p)%=p;
39         else (ans+=(1ll*A(K,L-n)*A(K-(L-n),n)%p*A(K-(L-n),n)%p-A(K,L-n+2)*A(K-(L-n+2),n-1)%p*A(K-(L-n+2),n-1)%p)%p)%=p;
40     }
41     printf("%lld\n",(ans%p+p)%p);
42     return 0;
43 }

 

转载于:https://www.cnblogs.com/guapisolo/p/10466041.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值