1. 程式人生 > 其它 >CF204E 【Little Elephant and Strings】

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);

完整程式碼就不放了,關鍵程式碼全部已經給出來了