题解:P14238 [CCPC 2024 Shandong I] 三角形
Priestess_SLG · · 题解
先观察性质,三个串
A=B\ge C
容易证明此时
A>B,C
此时
然后这个东西就算重了,考虑容斥掉重复计算的部分。可以发现若
时间复杂度为
namespace lowspeed_song {
mt19937_64 mt(time(0));
inline void init() {
}
int a[N];
struct BIT {
int tree[N];
inline BIT() { memset(tree, 0, sizeof tree); }
inline void clr(int x) { for (; x < N; x += (x &- x)) tree[x] = 0; }
inline void add(int x, int v) { for (; x < N; x += (x &- x)) tree[x] += v; }
inline int qry(int x) { int s = 0; for (; x; x -= (x &- x)) s += tree[x]; return s; }
inline int qry(int l, int r) { return qry(r) - qry(l - 1); }
} fwt;
char s[N];
namespace Suffix_Array {
int sa[N], rk[N], buc[N], height[N], n, idx, id[N], len[N];
// id[i]: 从位置 i 开始到下一个 # 为止的字符串 在所有不同字符串中的字典序的排名是多少
struct Node {
int x[2], buc;
inline Node() { x[0] = x[1] = buc = 0; }
inline Node(int a, int b, int c) { x[0] = b, x[1] = a, buc = c; }
} a[N], b[N];
inline void sort(int up) {
for (int i : {0, 1}) {
memset(buc, 0, sizeof buc);
for (int j = 1; j <= n; ++j) ++buc[a[j].x[i]];
for (int j = 1; j <= up; ++j) buc[j] += buc[j - 1];
for (int j = n; j; --j) b[buc[a[j].x[i]]--] = a[j];
for (int j = 1; j <= n; ++j) a[j] = b[j];
}
int idx = 1;
rk[a[1].buc] = 1;
for (int i = 2; i <= n; ++i)
if (a[i].x[0] == a[i - 1].x[0] && a[i].x[1] == a[i - 1].x[1]) rk[a[i].buc] = idx;
else rk[a[i].buc] = ++idx;
}
inline int build() {
for (int i = 1; i <= n; ++i) a[i] = Node(s[i], 1, i);
sort(128);
for (int i = 1; i < n; i <<= 1) {
for (int j = 1; j <= n; ++j) a[j] = Node(rk[j], (i + j <= n) ? rk[i + j] : 0, j);
sort(n);
} for (int i = 1; i <= n; ++i) sa[rk[i]] = i;
for (int i = 1; i <= n; ++i)
if (rk[i] == 1) height[rk[i]] = 0;
else {
int p = sa[rk[i] - 1], l = max<int>(0, height[rk[i - 1]] - 1);
while (i + l <= n && p + l <= n && s[i + l] == s[p + l]) ++l;
height[rk[i]] = l;
}
len[n + 1] = 0;
for (int i = n; i; --i) len[i] = (s[i] == '#') ? 0 : len[i + 1] + 1;
int las = -1, g = 0;
for (int i = 1; i <= n; ++i) if (s[sa[i]] != '#') {
if (!~las || len[sa[i]] != len[las] || height[i] < len[sa[i]]) ++g;
id[sa[i]] = g, las = sa[i];
} return g;
}
} // namespace Suffix_Array
#define SA Suffix_Array
struct TrieNode {
int ne[26], cnt, id;
inline TrieNode() { memset(ne, cnt = id = 0, sizeof ne); }
} tree[N];
int idx = 0, endpos[N];
inline void ins(string s, int id) {
int now = 0;
for (char &i : s) {
if (!tree[now].ne[i - 'a']) tree[now].ne[i - 'a'] = ++idx;
now = tree[now].ne[i - 'a'];
} ++tree[now].cnt, endpos[id] = now;
}
string str[N];
int st[N], pre[N], buc[N], vis[N], rk[N];
inline void sol([[maybe_unused]]int __testcase_id, [[maybe_unused]]int __testcase_sum) {
int n; cin >> n;
#define sidx SA::n
sidx = 0;
for (int i = 0; i < n; ++i) {
cin >> str[i];
st[i] = sidx + 1;
for (char &j : str[i]) s[++sidx] = j;
s[++sidx] = '#';
ins(str[i], i);
} int m = SA::build();
fill(buc, buc + m + 1, 0), fill(vis, vis + idx + 1, 0);
for (int i = 0; i < n; ++i) ++buc[SA::id[st[i]]], tree[endpos[i]].id = SA::id[st[i]];
pre[1] = 0; for (int i = 2; i <= m; ++i) pre[i] = pre[i - 1] + buc[i - 1];
i64 res = 0;
for (int i = 0; i < n; ++i) if (!vis[endpos[i]]) {
vis[endpos[i]] = 1;
int cnt = tree[endpos[i]].cnt, x = pre[SA::id[st[i]]], u = 0;
i64 now = 0;
vector<tuple<int, int, int>> event; // {r, t, c}
for (int j = 0; j + 1 < str[i].size(); ++j) {
u = tree[u].ne[str[i][j] - 'a'];
if (!tree[u].cnt) continue;
int a = tree[u].id, b = SA::id[st[i] + j + 1];
now += 1ll * tree[u].cnt * max<int>(0, x - pre[b] - buc[b]);
if (a > b) now -= 1ll * tree[u].cnt * (tree[u].cnt + 1) / 2;
event.emplace_back(a, b, tree[u].cnt);
} vector<int> ord(event.size(), 0); ranges::iota(ord, 0);
ranges::sort(ord, [&](int i, int j) { return get<1>(event[i]) < get<1>(event[j]); });
for (int i = 0; i < event.size(); ++i) rk[i] = get<0>(event[i]);
vector<int> fwt_used;
int pos = 0;
for (int j = 0; j < event.size(); ++j) {
while (pos < event.size() && get<1>(event[ord[pos]]) < get<0>(event[j]))
fwt.add(ord[pos] + 1, get<2>(event[ord[pos]])), fwt_used.emplace_back(ord[pos++] + 1);
int las = upper_bound(rk, rk + event.size(), get<1>(event[j])) - rk;
if (las < j) now -= 1ll * get<2>(event[j]) * fwt.qry(las + 1, j);
} for (int &i : fwt_used) fwt.clr(i);
res += 1ll * cnt * (cnt - 1) * (cnt - 2) / 6 + 1ll * cnt * (cnt - 1) / 2 * x + 1ll * now * cnt;
} cout << res << '\n';
for (int i = 0; i <= idx; ++i) tree[i] = TrieNode();
for (int i = 0; i <= n; ++i) endpos[i] = 0;
idx = 0, SA::idx = 0;
}
} // namespace lowspeed_song