Search for a command to run...
Progressive hints first, then the full explanation and implementation when you're ready to cash out.
Review status
AI-generated and still unreviewed. Double-check the details before internalizing them.
Hints
Open only as much as you need to keep the solve alive.
The answer is not really about strings. Replace “sum of squared frequencies” with “number of ordered pairs of generated paths whose strings are equal.” Squaring is basically yelling: count pairs.
For two fixed starting nodes , define a value for how many equal-string path pairs can start exactly at . If , it is . If the letters match, you can either stop both paths immediately, or jump both to strict descendants.
So the natural recurrence is
This looks huge, but the double descendant sum is just a rectangle sum over Euler-tour intervals.
Root the tree, compute Euler order, and let every subtree be a contiguous interval . Process nodes in reverse DFS order. Maintain a 2D table where is the sum of over subtree intervals, so descendant-pair sums become rectangle queries.
Once all are known, the answer for node is simply
Again, that is a rectangle sum over the same Euler-indexed 2D matrix. The whole thing fits in per test case, which is exactly where this problem wants you to land.
Reframe the Beast
For a fixed node , we have a multiset of strings produced by all valid descending paths fully inside . We need
That is the number of ordered pairs of paths inside that produce the same string. This is the main de-bullshit step. We do not need to store strings, hash strings, build a trie, or summon suffix-array dark magic.
A valid path is just a nonempty chain in the ancestor order:
where each next node is a strict descendant of the previous one. Jumps can skip levels.
So for two paths to produce the same string, they must have the same length and matching letters position by position.
Pair DP
Let
be the number of ordered pairs of valid paths that start exactly at nodes and and produce equal strings.
If , then no equal string can start there:
If , then the first characters match. After that, there are two choices:
Therefore:
This recurrence is clean, but a direct implementation would be way too slow: four nested subtree loops, aka performance murder with paperwork.
Euler Tour Saves the Day
Root the tree at . Run DFS and assign each node a time such that every subtree is a contiguous interval:
Now the sum
is just a rectangle sum over the matrix in Euler order:
Why ? In DFS preorder, the root of the subtree is first, so all strict descendants of occupy exactly the rest of that subtree interval.
Nice. Now we just need rectangle sums of already-computed values.
Order of Computation
The recurrence for only uses where is a strict descendant of and is a strict descendant of .
So if we process nodes in reverse Euler order, descendants are already done. For pairs, process Euler indices from down to in both dimensions. When computing the cell , every cell inside the descendant rectangle has larger Euler indices, so it is already known.
Let be the node at Euler position .
We maintain a 2D suffix-sum table over the Euler matrix:
where
A rectangle sum is then:
When we fill cells from bottom-right to top-left, we can compute suf[i][j] immediately after computing mat[i][j]:
So each cell is computed in .
Getting the Final Answers
For node , we need all ordered path pairs whose first nodes are both inside :
That is another rectangle sum over :
Use the same suffix table. Done.
Correctness Proof
We prove the algorithm computes every answer correctly.
First, consider . If , no pair of paths starting at and can produce equal strings, because their first characters already differ. The algorithm sets , which is correct.
Now suppose . Any equal-string pair of paths starting at and has matching first characters. After that, exactly one of two things happens: both paths stop, producing the one-character string, or both continue. They cannot have only one path continue, because then the string lengths would differ. If they continue, the first jumps must go to some strict descendants of and of , and the remaining suffixes must be an equal-string path pair starting at . There are such suffix-pairs. These choices are disjoint and cover all possibilities, so the recurrence is exact.
The computation order is valid because only depends on pairs where both nodes are strict descendants. In Euler preorder, strict descendants of a node appear later inside its subtree interval. Processing Euler positions in decreasing order guarantees all needed descendant-pair values are already computed.
The suffix-sum table correctly returns sums over any rectangle of the Euler-indexed matrix by the standard 2D inclusion-exclusion formula. Therefore the algorithm computes each recurrence value exactly.
Finally, for a fixed subtree root , every generated path starts at exactly one node . Thus every ordered pair of generated paths corresponds to exactly one ordered pair of starting nodes inside , and contributes to precisely when the produced strings are equal. Summing over all therefore equals . That is exactly the requested answer.
Complexity
There are pairs of nodes. Each pair is processed in using rectangle sums.
So each test case runs in
time and uses
memory. Since , this fits comfortably. Not free, but definitely not cursed.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
const int MOD = 998244353;
int addmod(int a, int b) {
int s = a + b;
if (s >= MOD) s -= MOD;
return s;
}
int submod(int a, int b) {
int s = a - b;
if (s < 0) s += MOD;
return s;
}
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
string s;
cin >> s;
s = " " + s;
vector<vector<int>> g(n + 1);
for (int i = 0; i < n - 1; i++) {
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
vector<int> tin(n + 1), tout(n + 1), at(n + 1), parent(n + 1);
int timer = 0;
vector<int> st = {1}, order;
parent[1] = -1;
while (!st.empty()) {
int u = st.back();
st.pop_back();
order.push_back(u);
for (int v : g[u]) {
if (v == parent[u]) continue;
parent[v] = u;
st.push_back(v);
}
}
for (int u : order) {
tin[u] = ++timer;
at[timer] = u;
}
for (int idx = n - 1; idx >= 0; idx--) {
int u = order[idx];
tout[u] = tin[u];
for (int v : g[u]) {
if (parent[v] == u) tout[u] = max(tout[u], tout[v]);
}
}
int N = n + 2;
vector<vector<int>> mat(N, vector<int>(N, 0));
vector<vector<int>> suf(N + 1, vector<int>(N + 1, 0));
auto rect = [&](int x1, int y1, int x2, int y2) -> int {
if (x1 > x2 || y1 > y2) return 0;
int res = suf[x1][y1];
res = submod(res, suf[x2 + 1][y1]);
res = submod(res, suf[x1][y2 + 1]);
res = addmod(res, suf[x2 + 1][y2 + 1]);
return res;
};
for (int i = n; i >= 1; i--) {
int u = at[i];
for (int j = n; j >= 1; j--) {
int v = at[j];
if (s[u] == s[v]) {
mat[i][j] = addmod(1, rect(i + 1, j + 1, tout[u], tout[v]));
}
int val = mat[i][j];
val = addmod(val, suf[i + 1][j]);
val = addmod(val, suf[i][j + 1]);
val = submod(val, suf[i + 1][j + 1]);
suf[i][j] = val;
}
}
for (int u = 1; u <= n; u++) {
cout << rect(tin[u], tin[u], tout[u], tout[u]) << ' ';
}
cout << '\n';
}
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
const int MOD = 998244353;
int addmod(int a, int b) {
int s = a + b;
if (s >= MOD) s -= MOD;
return s;
}
int submod(int a, int b) {
int s = a - b;
if (s < 0) s += MOD;
return s;
}
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
string s;
cin >> s;
s = " " + s;
vector<vector<int>> g(n + 1);
for (int i = 0; i < n - 1; i++) {
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
vector<int> tin(n + 1), tout(n + 1), at(n + 1), parent(n + 1);
int timer = 0;
vector<int> st = {1}, order;
parent[1] = -1;
while (!st.empty()) {
int u = st.back();
st.pop_back();
order.push_back(u);
for (int v : g[u]) {
if (v == parent[u]) continue;
parent[v] = u;
st.push_back(v);
}
}
for (int u : order) {
tin[u] = ++timer;
at[timer] = u;
}
for (int idx = n - 1; idx >= 0; idx--) {
int u = order[idx];
tout[u] = tin[u];
for (int v : g[u]) {
if (parent[v] == u) tout[u] = max(tout[u], tout[v]);
}
}
int N = n + 2;
vector<vector<int>> mat(N, vector<int>(N, 0));
vector<vector<int>> suf(N + 1, vector<int>(N + 1, 0));
auto rect = [&](int x1, int y1, int x2, int y2) -> int {
if (x1 > x2 || y1 > y2) return 0;
int res = suf[x1][y1];
res = submod(res, suf[x2 + 1][y1]);
res = submod(res, suf[x1][y2 + 1]);
res = addmod(res, suf[x2 + 1][y2 + 1]);
return res;
};
for (int i = n; i >= 1; i--) {
int u = at[i];
for (int j = n; j >= 1; j--) {
int v = at[j];
if (s[u] == s[v]) {
mat[i][j] = addmod(1, rect(i + 1, j + 1, tout[u], tout[v]));
}
int val = mat[i][j];
val = addmod(val, suf[i + 1][j]);
val = addmod(val, suf[i][j + 1]);
val = submod(val, suf[i + 1][j + 1]);
suf[i][j] = val;
}
}
for (int u = 1; u <= n; u++) {
cout << rect(tin[u], tin[u], tout[u], tout[u]) << ' ';
}
cout << '\n';
}
}