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);
完整代码就不放了,关键代码全部已经给出来了