【洛谷4965】薇尔莉特的打字机(假装有棵Trie树)

点此看题面

  • 一开始有一个长度为(n)的字符串,有(m)次操作:
    • 加入一个给定字母。
    • 删除最后一个字母(如果串为空则忽略)。
  • 已知每次操作有可能成功有可能失败,问最后可能得到多少种串。
  • (n,mle5 imes10^6)

(Trie)

考虑暴力的做法,我们直接建出一棵(Trie)树。

如果删去一个字符,就是跳回父节点,发现除初始链外所有节点都是原本就能得到的,因此这一操作就相当于初始链上有一个新的节点可以扩展儿子了。

每次加入一个新的字符,就给所有没这个儿子的节点(初始链上尚未更新到的节点除外)加上这个儿子,并更新答案。

假装有棵(Trie)

实际上我们并不需要知道到底有多少(Trie)树,只要知道对于每种字符,有多少个节点没这个儿子。

(f_i)表示没有第(i)个儿子的节点数。

加入一个字符(x),会新增(f_x)个节点,故除(f_x)以外的所有(f_i)加上(f_x)

删去一个字符,初始链的上一个节点就可以扩展儿子,因此给除了已有儿子以外的(25)(f_i)(1)

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 5000000
#define X 19260817
#define Inc(x,y) ((x+=(y))>=X&&(x-=X))
using namespace std;
int n,m,f[30];char s[N+5],ss[N+5];
int main()
{
	RI i,j,t,ans=1;for(scanf("%d%d%s%s",&n,&m,ss+1,s+1),i=1;i<=26;++i) f[i]=1;for(i=1;i<=m;++i)
	{
		if(s[i]=='u') {if(n) for(--n,++ans,j=1;j<=26;++j) f[j]+=(ss[n+1]&31)!=j;continue;}//初始链上跳
		for(t=f[s[i]&31],Inc(ans,t),j=1;j<=26;++j) (s[i]&31)^j&&Inc(f[j],t);//新增节点数即为没有该儿子的节点数
	}return printf("%d
",ans),0;
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu4965.html