[Luogu P4248] [BZOJ 3238] [AHOI2013]差異
阿新 • • 發佈:2018-12-10
洛谷傳送門
題目描述
給定一個長度為 的字串 ,令 表示它從第 個字元開始的字尾。求
其中, 表示字串 的長度, 表示字串 和字串 的最長公共字首。
輸入輸出格式
輸入格式:
一行,一個字串 。
輸出格式:
一行,一個整數,表示所求值。
輸入輸出樣例
輸入樣例#1:
cacao
輸出樣例#1:
54
說明
對於 100% 的資料,保證 ,且均為小寫字母。
解題分析
題目求的是字尾的公共字首, 我們把串倒過來, 就變成了求字首的最長公共字尾。
在裡每個點都包含了字首, 而顯然其公共字尾就是在樹上面的LCA。
所以我們直接在樹上從下往上就好了。
至於這個玩意
顯然就等於。程式碼如下:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cctype>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 1000050
#define ll long long
int l, cnt, cur, last;
ll ans, num[MX];
int to[MX][26], len[MX], par[MX], siz[MX], buc[MX], ord[MX];
char dat[MX];
namespace SAM
{
IN void insert(R int id)
{
R int now, tar;
cur = ++cnt; len[cur] = len[last] + 1; siz[cur] = 1;
for (now = last; ~now; now = par[now])
{
if(to[now][id]) break;
to[now][id] = cur;
}
if(now < 0) return last = cur, par[cur] = 0, void();
tar = to[now][id];
if(len[tar] == len[now] + 1) return last = cur, par[cur] = tar, void();
int nw = ++cnt; std::memcpy(to[nw], to[tar], sizeof(to[tar]));
par[nw] = par[tar], par[tar] = nw; len[nw] = len[now] + 1;
for (; (~now) && to[now][id] == tar; now = par[now]) to[now][id] = nw;
par[cur] = nw; last = cur;
}
IN ll calc()
{
R int now; ll ret = 0;
for (R int i = 1; i <= cnt; ++i) ++buc[len[i]];
for (R int i = 1; i <= l; ++i) buc[i] += buc[i - 1];
for (R int i = 1; i <= cnt; ++i) ord[buc[len[i]]--] = i;
for (R int i = cnt; i; --i)
{
now = ord[i];
if(par[now] > 0)
ret += 1ll * siz[now] * siz[par[now]] * len[par[now]], siz[par[now]] += siz[now];
}
return ret * 2;
}
}
int main(void)
{
scanf("%s", dat + 1); l = std::strlen(dat + 1);
ans = 1ll * (l - 1) * l / 2 * (l + 1); std::reverse(dat + 1, dat + 1 + l);
par[0] = -1;
for (R int i = 1; i <= l; ++i) SAM::insert(dat[i] - 'a');
printf("%lld", ans - SAM::calc());
}