CF204E 【Little Elephant and Strings】

tag:SAM,倍增


贡献一个用广义\(SAM\),不用大力数据结构的做法

把问题分成两部分解决

  • 求一个字符串在多少个\(a_i\)中出现过
  • 枚举一个串的一个点\(i\),求以\(i\)为右端点的,在至少\(k\)\(a_i\)中出现过的字符串个数

Case 1

对于第一个问题,可以建广义\(SAM\),记录\({a_i}_j\)\(SAM\)上对应的节点为\({pos_i}_j\)

然后对于一个串\(a_i\)来说,它的贡献相当于是所有\({pos_i}_j\)\(SAM\)上构成的一棵虚树,虚树每个节点sz+1

那么具体实现可以用差分,按\(dfs\)序排序,每个\(pos\)\(sz\)+1,然后每对相邻的\(pos\)\(lca\)\(sz\)-1

inline bool cmp(const int &u, const int &v){return dfn[u]<dfn[v];}

for(register int i=1; i<=n; i++){
    sort(pos[i]+1,pos[i]+len[i]+1,cmp);
    for(register int j=1; j<=len[i]; j++) sz[pos[i][j]]++;
    for(register int j=1; j<len[i]; j++) sz[lca(pos[i][j],pos[i][j+1])]--;
}

然后跑个\(dfs\)累加差分数组


void addmk(int x){
	for(register int u=fst[x]; u; u=edge[u].nxt) 
    	addmk(edge[u].to),
        sz[x] += sz[edge[u].to];
}

然后就求出了每个节点的出现次数

Case 2

考虑一个很暴力的做法
while(x and sz[x]<K) x = fa(x);
即找到第一个\(sz\geq K\)的祖先然后把\(len\)累加进答案

实际上这个过程可以用倍增去代替,因为越往上走,出现的次数肯定越多。所以用类似于求\(lca\)的做法,先跳到最上面的一个\(sz< K\)的位置,然后再跳一步

int x = pos[i][j];
for(register int k=Log[dep[x]]; compl k; k--) 
	if(sz[fa[k][x]]<K) x = fa[k][x]; 
if(sz[x]<K) x = fa[0][x];
ans += len(x);

完整代码就不放了,关键代码全部已经给出来了

posted @ 2021-07-01 15:14  oisdoaiu  阅读(40)  评论(0编辑  收藏  举报