bzoj
洛谷考慮乙個形如\(aabb\)的串是由兩個形如\(aa\)的串拼起來的
那麼我們設
\(f[i]\):以位置\(i\)為結尾的形如\(aa\)串的個數
\(g[i]\):以位置\(i\)為開頭的形如\(aa\)串的個數
\[\therefore ans=\sum_^nf[i]*g[i+1]
\]題目的難點轉化為求\(f,g\)。
但是,其實我們只要\(o(n^2)\)暴力求一下就有\(95pts\)了,
所以我們接下來考慮最後的\(5pts\)怎麼拿:
我們列舉\(a\)的長度\(len\)
將所有位置為\(len\)的倍數的點設為關鍵點,
則如果乙個\(aa\)滿足要求
這個\(aa\)必過兩個關鍵點,
那麼我們要算的就是相鄰兩個關鍵點對答案的貢獻:
記相鄰兩個關鍵點為\(,i,j\)那麼\(j=i+len\)
記\(lcp=lcp(suf(i), suf(j)),lcs=lcs(pre(i-1),pre(j-1))\)
那麼,如果\(lcp+lcs,則不能構成\(aa\)
為什麼呢?
相當於這樣一種情況:
\[\underbrace_\;\overbrace_\;....\underbrace_}^\;\underbrace_
\]這樣子是不合法的。
反之,中間兩段的\(lcp,lcs\)會有交,而我們這個\(a\)串的終點落在中間長度為\(lcp+lcs-len+1\)的交上都是可以的
因為這樣的話平移一下可以保證緊跟著出現乙個不重疊的\(a\)串
又因為串\(a\)起點和終點分別出現的位置是一段區間,所以直接分別在\(f,g\)上差分即可
複雜度是調和級數\(o(nlogn)\)。
具體細節看**:
#include #include #include #include #include #include #include using namespace std;
inline int gi()
const int max_n = 3e4 + 5;
char a[max_n];
int n, lg[max_n], f[max_n], g[max_n];
struct suffixarray
for (int i = 1; i <= n; i++) rnk[sa[i]] = i;
for (int i = 1, j = 0; i <= n; i++)
} int st[16][max_n];
void buildst()
int query(int l, int r)
} a, b;
void sol()
} } for (int i = 1; i <= n; i++) f[i] += f[i - 1], g[i] += g[i - 1];
long long ans = 0;
for (int i = 1; i < n; i++) ans += 1ll * f[i] * g[i + 1];
printf("%lld\n", ans);
} int main ()
bzoj 4650 Noi2016 優秀的拆分
原來只會兩個log平衡樹合併。後來圍觀claris在uoj群上秒題後會了這道題。列舉a b 的長度l,然後列舉i kl,考慮字首i,i l和字尾i 1,i l 1,求出字首的lcp和字尾的lcp,然後合法的方案就在乙個方案內。差分一下即可。注意要避免重複。ac 如下 include include ...
BZOJ 4650 Noi2016 優秀的拆分
題解 求解每個位置向左向右aa串的個數f x g x 列舉a的長度,每a個位置設乙個關鍵點 每乙個a一定僅且跨越乙個關鍵點 然後求出相鄰關鍵點向前向後的最長公共字首的長度,這會對一段區間的f,g產生影響 用差分 字首和統計答案 include include include includeusing...
BZOJ4650 Noi2016 優秀的拆分
設 f i 表示以 i 結尾的square個數,g i 表示以 i 開頭的square個數,則 ans sum f ig 列舉square中長度的一半 l 每 l 步取乙個關鍵點,那麼每個該長度的square的肯定恰好經過兩個相鄰的關鍵點,且位置差距為 l 的字元一一匹配。所以相鄰關鍵點之間通過字尾...