P4248 [AHOI2013]差異
阿新 • • 發佈:2018-12-22
\(\color{#0066ff}{題目描述}\)
給定一個長度為 \(n\) 的字串 \(S\),令 \(T_i\) 表示它從第 \(i\) 個字元開始的字尾。求
\(\displaystyle \sum_{1\leqslant i<j\leqslant n}\text{len}(T_i)+\text{len}(T_j)-2\times\text{lcp}(T_i,T_j)\)
其中,\(\text{len}(a)\) 表示字串 \(a\) 的長度,\(\text{lcp}(a,b)\) 表示字串 \(a\) 和字串 \(b\) 的最長公共字首。
\(\color{#0066ff}{輸入格式}\)
一行,一個字串 \(S\)。
\(\color{#0066ff}{輸出格式}\)
一行,一個整數,表示所求值。
\(\color{#0066ff}{輸入樣例}\)
cacao
\(\color{#0066ff}{輸出樣例}\)
54
\(\color{#0066ff}{資料範圍與提示}\)
對於 100% 的資料,保證 \(2\leqslant n\leqslant 500000\),且均為小寫字母
\(\color{#0066ff}{題解}\)
定義H為排名為i-1和i的字串的LCP
我們有\(H[i] \geq H[i-1] - 1\)
還有\(LCP(i,j)=min(LCP(i,k),LCP(k,j))\)
\(\begin{aligned}LCP(i,j)=\min_{i+1\leq k \leq j}(H[k]) \end{aligned}\)
這裡就不證明了其實我不會
所以把大的\(\Sigma\)拆開
前兩部分可以直接統計
後面的LCP可以先求出H,然後單調棧求出每個數作為最小值對於區間的貢獻
不看LL見祖宗
#include <bits/stdc++.h> typedef long long LL; const int maxn = 5e5 + 100; const int inf = 0x7fffffff; int n ,m; int H[maxn], c[maxn], x[maxn], y[maxn], sa[maxn], rk[maxn]; LL l[maxn], r[maxn]; char s[maxn]; LL ans1, ans2, ans3; class stack { private: int st[maxn], tp; public: void pop() { tp--; } int top() { return st[tp]; } void push(int x) { st[++tp] = x; } void clr() { memset(st, 0, sizeof st); } }S; LL getans(LL t) { return (t * (t + 1LL)) >> 1LL; } void predoit() { for(int i = 1; i <= n; i++) c[x[i] = s[i]]++; for(int i = 1; i <= m; i++) c[i] += c[i - 1]; for(int i = n; i >= 1; i--) sa[c[x[i]]--] = i; for(int k = 1; k <= n; k <<= 1) { int num = 0; for(int i = n - k + 1; i <= n; i++) y[++num] = i; for(int i = 1; i <= n; i++) if(sa[i] > k) y[++num] = sa[i] - k; for(int i = 1; i <= m; i++) c[i] = 0; for(int i = 1; i <= n; i++) c[x[i]]++; for(int i = 1; i <= m; i++) c[i] += c[i - 1]; for(int i = n; i >= 1; i--) sa[c[x[y[i]]]--] = y[i], y[i] = 0; std::swap(x ,y); x[sa[1]] = 1, num = 1; for(int i = 2; i <= n; i++) x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k])? num : ++num; if(num == n) break; m = num; } for(int i = 1; i <= n; i++) rk[i] = x[i]; int h = 0; for(int i = 1; i <= n; i++) { if(rk[i] == 1) continue; if(h) h--; int j = sa[rk[i] - 1]; while(j + h <= n && s[i + h] == s[j + h]) h++; H[rk[i]] = h; } } void work_contribute() { S.push(1); H[1] = -inf; for(int i = 2; i <= n; i++) { while(H[S.top()] >= H[i]) S.pop(); l[i] = S.top(); S.push(i); } S.clr(); S.push(n + 1); H[n + 1] = -inf; for(int i = n; i >= 2; i--) { while(H[S.top()] > H[i]) S.pop(); r[i] = S.top(); S.push(i); } for(LL i = 2LL; i <= n; i++) ans3 += (i - l[i]) * (r[i] - i) * H[i]; } void query() { ans3 <<= 1LL; for(LL i = n; i >= 1; i--) ans1 += i * (i - 1LL); for(int i = n - 1; i >= 1; i--) ans2 += getans(i); printf("%lld", ans1 - ans3 + ans2); } int main() { scanf("%s", s + 1); n = strlen(s + 1), m = 122; predoit(); work_contribute(); query(); return 0; }