「ZJOI2015」諸神眷顧的幻想鄉
阿新 • • 發佈:2020-08-20
知識點: 廣義 SAM,暴力
原題面 Loj Luogu
扯
萌妹子幽香有多萌
題意簡述
給定一棵 \(n\) 個節點的樹,每個節點都有一個字元 \(c_i\)。
可任意選擇兩個點 \(u,v\),路徑 \(u\rightarrow v\) 上的字元構成一個字串(\(v\rightarrow u\) 構成的字串可能不同)。
求所有可以構成的 不同的字串的數量。
\(1\le n\le 10^5, 1\le c_i\le 10\),最多存在 \(20\) 個葉節點。
分析題意
發現 任意 選擇兩個節點構成的所有字串,等價於選擇兩個 葉節點 構成的字串的 所有子串。
如果給定的是一條鏈,就非常好做了。
直接將整個串正反插入廣義 SAM 中,求 \(\sum\operatorname{len}(i) - \operatorname{len}(\operatorname{link}(i))\)
但是給不得,只能來猜結論了。
考慮怎麼把彎曲的路徑掰♂直。
發現 任何一條路徑,都會在葉節點到其他葉節點 構成的路徑中出現。
題中給了一個很特別的性質:最多存在 \(20\) 個葉節點。
考慮暴力列舉選擇兩個 葉節點 構成的字串,並將它們插入廣義 SAM 中,求 \(\sum\operatorname{len}(i) - \operatorname{len}(\operatorname{link}(i))\) 即為答案。
複雜度大概是 \(O(\text{leaves_num}\times n)\),可過。
程式碼實現
//知識點:廣義 SAM,暴力 /* By:Luckyblock */ #include <algorithm> #include <cctype> #include <cstdio> #include <cstring> #define ll long long const int kMaxn = 1e5 + 10; const int kMaxm = 10; //============================================================= ll ans; char S[kMaxn]; bool is_leaves[kMaxn]; int leaves_num, leaves[50]; int edge_num, c[kMaxn], into[kMaxn], head[kMaxn], v[kMaxn << 1], ne[kMaxn << 1]; int node_num = 1, ch[kMaxn << 5][kMaxm], len[kMaxn << 5], link[kMaxn << 5]; //============================================================= inline int read() { int f = 1, w = 0; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1; for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0'); return f * w; } void AddEdge(int u_, int v_) { v[++ edge_num] = v_, ne[edge_num] = head[u_], head[u_] = edge_num; } int Insert(int c_, int last_) { if (ch[last_][c_]) { int p = last_, q = ch[p][c_]; if (len[p] + 1 == len[q]) return q; int newq = ++ node_num; memcpy(ch[newq], ch[q], sizeof(ch[q])); len[newq] = len[p] + 1; link[newq] = link[q]; link[q] = newq; for (; p && ch[p][c_] == q; p = link[p]) ch[p][c_] = newq; return newq; } int p = last_, now = ++ node_num; len[now] = len[p] + 1; for (; p && ! ch[p][c_]; p = link[p]) ch[p][c_] = now; if (! p) {link[now] = 1; return now;} int q = ch[p][c_]; if (len[q] == len[p] + 1) {link[now] = q; return now;} int newq = ++ node_num; memcpy(ch[newq], ch[q], sizeof(ch[q])); link[newq] = link[q], len[newq] = len[p] + 1; link[q] = link[now] = newq; for (; p && ch[p][c_] == q; p = link[p]) ch[p][c_] = newq; return now; } void Dfs(int u_, int fa_, int dep_) { S[dep_] = c[u_]; if (is_leaves[u_] && dep_ != 1) { int last = 1; for (int i = 1; i <= dep_; ++ i) last = Insert(S[i], last); return ; } for (int i = head[u_]; i; i = ne[i]) { if (v[i] != fa_) Dfs(v[i], u_, dep_ + 1); } } //============================================================= int main() { int n = read(), Marisa = read(); for (int i = 1; i <= n; ++ i) c[i] = read(); for (int i = 1; i < n; ++ i) { int u = read(), v = read(); AddEdge(u, v), AddEdge(v, u); into[u] ++, into[v] ++; } for (int i = 1; i <= n; ++ i) { if (into[i] == 1) { leaves[++ leaves_num] = i; is_leaves[i] = true; } } for (int i = 1; i <= leaves_num; ++ i) { Dfs(leaves[i], 0, 1); } for (int i = 2; i <= node_num; ++ i) { ans += len[i] - len[link[i]]; } printf("%lld\n", ans); return 0; }