Gym102331G Grammarly

Link
如果我们把所有路径以树的形式画出来,那么这会构成一棵二叉树,我们要计算的就是二叉树的节点数。
一个节点有两个儿子当且仅当这个节点上的字符串出现了至少两种字符。
我们称一个字符串全等当且仅当该字符串中只出现过一种字符。
考虑一个极长全等子串(s_{l,r}),我们有({l-1+n-rchoose l-1})条路径上第一次到达的全等子串为该串,同时该字符串下是一条长度为(r-l+1)的链,贡献为({l-1+n-rchoose l-1}(r-l+1))
同时我们还要枚举它的每一个前缀/后缀,计算有多少条路径上第一次到达的全等子串为该前缀/后缀(即强制要求最后一个删去的字符为(s_{l-1})/(s_{r+1})),这里的贡献为:
前缀:(sumlimits_{i=l}^{r-1}{l-2+n-ichoose l-2}(i-l+1)),后缀:(sumlimits_{i=l+1}^r{i-2+n-rchoose i-1}(r-i+1))
此时没有计算的就是非全等字符串的节点。
这里的答案是(sumlimits_{[l,r] ext{contains at least two different characters}}{n-r+l-1choose l-1})
考虑枚举左端点(l),设最近的合法右端点为(p_l)(显然这东西我们早就求出来了),那么答案就是(sumlimits_{l=1}^nsumlimits_{r=p_l}^n{n-r+l-1choose l-1}=sumlimits_{l=1}^nsumlimits_{i=0}^{n-p_l}{l-1+ichoose l-1}=sumlimits_{l=1}^n{l+n-p_lchoose l})

#include<cstdio>
#include<cstring>
const int N=600007,P=998244353;
int n,ans,fac[N],inv[N],ifac[N];char s[N];
void mod(int&x){x-=P,x+=x>>31&P;}
int C(int n,int m){return m<0||n<m? 0:1ll*fac[n]*ifac[m]%P*ifac[n-m]%P;}
int main()
{
    scanf("%s",s+1),n=strlen(s+1),fac[0]=fac[1]=ifac[0]=ifac[1]=inv[0]=inv[1]=1;
    for(int i=2;i<=600000;++i) fac[i]=1ll*fac[i-1]*i%P,inv[i]=1ll*inv[P%i]*(P-P/i)%P,ifac[i]=1ll*ifac[i-1]*inv[i]%P;
    int ans=0;
    for(int l=1,r;l<=n;l=r+1)
    {
	for(r=l;r<n&&s[r+1]==s[l];++r);
	mod(ans+=1ll*C(l-1+n-r,l-1)*(r-l+1)%P) ;
	for(int i=l;i<r;++i) mod(ans+=1ll*C(l-2+n-i,l-2)*(i-l+1)%P);
	for(int i=l+1;i<=r;++i) mod(ans+=1ll*C(i-2+n-r,i-1)*(r-i+1)%P);
	for(int i=l;i<=r;++i) mod(ans+=C(i-1+n-r,i));
    }
    printf("%d",ans);
}
原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12243013.html