【字尾陣列/SAM+邊分樹合併】LGP5115 Check,Check,Check one two!
【題目】
原題地址
給定一個字串
,求
其中
表示第
個字尾和第
個字尾的
,
表示第
個字首和第
個字首的
。
【解題思路】
解法一:字尾陣列
由於題目中定義的
和
是以一個點為中心往兩邊計算的,我們不妨將它們拼在一起。具體來說,假設我們知道
和
,那麼我們不難得到:
,且
。
於是我們對
建出字尾陣列,那麼我們要考慮的就是每兩個字尾之間的貢獻。顯然兩個字尾
有貢獻當且僅當它們的
不為
且
。
這樣做法就比較顯然了:每個字尾
要記錄
是什麼字元,按
從大到小拆隔板進行啟發式合併,考慮有多少對貢獻的時候直接容斥一下,然後乘上長度為當前
的貢獻即可。
現在我們要求長度
的串貢獻是多少。
實際上它的貢獻就是
(可能會有一些偏差以及邊界問題,可以看程式碼)
這個東西可以預處理出來,這樣我們就可以解決這道題了。
解法二:
邊分樹合併
我們對這個串的正序和逆序分別建出
,考慮答案的計算。
根據
的性質,兩個字首節點的
就是這兩個節點
的
,字尾同理,那麼如果把第一棵樹大於
的節點置零,第二棵同理,問題就轉化為了給定兩棵樹,詢問所有點對
在兩棵樹上
的
的乘積之和。
考慮在第一棵樹上列舉 進行貢獻,那麼可以一個個合併 的每個子樹,考慮跨越兩個連通塊的點對在第二棵樹上的 的 值。
這個東西用邊分樹可以解決,具體點來講我們將深度較低的一個聯通塊看成是右兒子而深度較大的一個聯通塊看成左兒子,然後每一個邊分樹節點維護一下左兒子中的節點個數和右兒子聯通塊中可能成為lca節點的點權和,然後合併的時候相乘一下就可以統計貢獻了。
但這個東西我並不會寫,照著別人的程式碼寫,但發現重構樹的時候我用自己的寫法在合併答案的時候好像出現了一些小差錯(WA了一個點),我並不知道為什麼。希望有好心人來解釋一下qwq。
【參考程式碼1】(SA)
#include<bits/stdc++.h>
#define mkp make_pair
#define fi first
#define se second
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned long long ull;
const int N=1e5+10;
int n,k1,k2;
int a[N],lp[N],rp[N],siz[N],bl[N],cnt[N][28];
char s[N];
pii h[N];
ull ans,f[N];
namespace SA
{
int rk[N],hi[N];
int wa[N],wb[N],wx[N],wy[