事情的相似度(SAM LCT 樹狀陣列)

2021-10-07 02:50:50 字數 2042 閱讀 4516

蒟蒻的第一道字串大題

此題的題意是求編號為一段區間的字首的最長公共字尾的長度

而這個最長公共字尾是可以超過這個區間限制的(被坑了好久。。。)

那麼這題就比較有思路了

我們可以考慮一下暴力

首先,我們對於每乙個字首[1,i],算出所有的字首[1,1~i-1]與它的最長公共字尾的大小,存到乙個vector裡面

查詢的時候就只查[l,r]中每個點x的vector裡面的[l,x-1]的答案,取最大值即可

這樣做是o(qn^2)的

考慮最長公共字尾的本質,不難聯想到sam

先對原串建出sam,在sam上,兩個點在fail樹上的lca的len值表示了這兩個點的最長公共字尾的長度

乙個字首的所有字尾表示在fail樹上就是該字首的對應點x到根節點的一條鏈

再開一下腦洞,聯想到[lnoi2014]lca那道題

考慮離線詢問,對於右端點 r 處理出所有左端點 l 的答案

我們把所有的1~r-1的字首對應點x到根的路徑依次做乙個標記覆蓋

(這其實是在貪心,因為越靠後的字首越可能影響更多的查詢)

然後我們查詢:在字首r的對應點xr到根的路徑上,覆蓋時間大於等於 l 的len值最大的點

直接做這個問題會非常麻煩,因為max不能字首相減,你還需要用二維線段樹來可持久化樹鏈剖分

然而這並沒有發揮我們離線的優勢

考慮再維護乙個樹狀陣列,來記錄當前r對應的每個左端點的答案

利用lct來維護鏈覆蓋與標記的下傳,在access的過程中更新樹狀陣列

每次都只更新跳鏈時跳向的那乙個點的權值

這時有人就會問,萬一中間有乙個點的位置更靠後,而這種做法沒有更新到這個點怎麼辦?

其實你access之前,你到根需要跳多少次鏈,你就經過了多少種不同的鏈標記

如圖,鏈3在access的時候,會經過兩種不同的標記,兩種標記都會被更新到

所以不用擔心漏掉某個點沒更新的情況

注意,標記下傳之後不要清零,因為標記剛打上去會被splay前的pdpath傳到子樹中

下一次直接訪問到該點的時候就沒有標記來更新了,這樣可以減小常數

如果覺得難以理解,可以在pushdown的時候更新樹狀陣列,這樣做應該是可以清零標記的,只不過常數會比較大

**:o(nlog^2n)

#include#include#include#includeusing namespace std;

#define n 200005

int n,m;

int pos[n],ans[n];

namespace sam

} las=np;

}}//----sam----

int tra[n];

void upd(int x,int k)

}int qry(int x)

return ret;

}namespace lct

bool nrt(int x)

void rot(int x)

void pushdown(int x)

} void pdpath(int x)

void splay(int x)

void acc(int x,int k)

}}//----lct----

vector> g[n];

int main()

for(i=1;i<=sam::tot;i++)lct::fa[i]=sam::fa[i];

for(i=1;i<=m;i++)

for(i=1;i<=n;i++)

for(i=1;i<=m;i++)

printf("%d\n",ans[i]);

}

LOJ 6041 事情的相似度

考慮暴力做法,離線詢問 因為兩個串的最長公共字尾,就是所代表節點的 lca 的 len 每一次加入乙個字首,在 parent 樹上往上跳,如果乙個點被跳過我們就更新答案 因為右端點固定時,左端點越大,對詢問的貢獻肯定越多,所以直接覆蓋掉這個節點事件的下標 也就是 pos 所以我們維護這個節點子樹內的...

雅禮集訓2017Day7事情的相似度

bitset幫助離線合併實現o n w 然後暴力就可以在n 100000時為所欲為 其實還是有點思想,從大到小列舉height值,這樣每次涉及到的問題的答案一定是height 另外脫機會mle?那就分兩次離唄。沒有什麼是一次離線解決不了的,如果有,那就兩次。從height大到小更新可以簡化轉移,因為...

LOJ 6041 事情的相似度(字尾自動機)

loj mbox 搬了這道題目。mbox 還是用到 lcp 就是 parent 樹上的 lca 的 len 每次詢問顯然就是區間內點的貢獻。那麼考慮所有可能出現的點對。顯然對於兩個子串而言,只會匹配最靠近的兩個。那麼用 set 維護 endpos 集合,每次合併的時候將兩個最靠近的位置合併成為乙個點...