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.
Forget the huge products. For “is it a square?”, only the parity of each prime exponent matters. So every is basically a bitmask of primes with odd exponent.
Look at one fixed unordered triple . In a tree, the three paths , , and form a Y-shape. There is one special vertex where the three branches meet: the median of the triple.
Count how many times each vertex on that Y-shape appears in the product . Almost everything appears exactly twice. The meeting vertex appears exactly three times. Vertices outside appear zero times.
Since “twice” contributes an even exponent and disappears modulo squares, the whole product is a perfect square iff the value at the median vertex is itself a perfect square. That’s the whole trick. The rest is just counting triples by their median.
For a vertex , remove it from the tree. Its connected components have sizes . A triple has median iff either it contains and chooses the other two vertices from two different components, or it does not contain and chooses three vertices from three different components.
The condition looks number-theory-heavy, but the tree structure nukes almost all of it.
The Square Part
For a product to be a perfect square, every prime must have an even exponent. So we only care about exponents modulo .
Now take any three distinct vertices . In a tree, the three paths between them meet at exactly one vertex. Call it . This is the usual tree median: the unique common vertex of the paths , , and .
Consider the product
Look at how many times each vertex value is multiplied in.
Every vertex on one of the three arms from to appears on exactly two of the three pair paths. That includes the endpoints unless one of them is .
The median vertex appears on all three paths.
So modulo parity:
Therefore the whole product has the same square/parity behavior as just .
So:
That’s the key. The products were bait. Pretty rude bait, honestly.
So now the problem is:
Count unordered triples of vertices whose tree median has a perfect-square value.
Counting Triples With Fixed Median
Fix a vertex . Suppose we remove from the tree. The remaining graph splits into connected components with sizes
These components are exactly the neighbor-side subtrees around .
A triple has median in two possible ways.
Case 1: The triple contains
Then the other two vertices must lie in two different components around .
If both were in the same component, the path between them would not need to pass through , so would not be the median.
The count is
Case 2: The triple does not contain
Then the three vertices must lie in three different components around .
The count is
So if is a perfect square, vertex contributes
If is not a perfect square, it contributes .
Getting Component Sizes Fast
Root the tree anywhere, say at .
Compute subtree sizes .
For a vertex , each child contributes one component of size after removing .
If has a parent, the parent-side component has size
So we can build the list of component sizes around every vertex in total .
Computing the Sums
For sizes :
Pair sum:
Triple sum:
We can compute both in one pass:
Maintain:
When adding a new size :
So add both to the answer, then update:
Only do this for vertices whose is a perfect square.
Perfect Square Check
Since , just compute
and check or to dodge floating-point nonsense.
Complexity
Each test case is linear:
The total over all tests is at most , so this easily fits. No prime factorization needed. The tags tried to sell you number theory; the tree median did the actual work.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
static bool isSquare(int x) {
int r = (int) sqrt((long double) x);
while ((ll)(r + 1) * (r + 1) <= x) ++r;
while ((ll)r * r > x) --r;
return (ll)r * r == x;
}
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
vector<int> a(n + 1);
for (int i = 1; i <= n; i++) cin >> a[i];
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> parent(n + 1, 0), order;
order.reserve(n);
stack<int> st;
st.push(1);
parent[1] = -1;
while (!st.empty()) {
int v = st.top();
st.pop();
order.push_back(v);
for (int to : g[v]) {
if (to == parent[v]) continue;
parent[to] = v;
st.push(to);
}
}
vector<int> sub(n + 1, 1);
for (int i = n - 1; i >= 0; i--) {
int v = order[i];
for (int to : g[v]) {
if (parent[to] == v) sub[v] += sub[to];
}
}
ll ans = 0;
for (int x = 1; x <= n; x++) {
if (!isSquare(a[x])) continue;
ll sum1 = 0;
ll sum2 = 0;
auto addComponent = [&](ll s) {
ans += sum1 * s;
ans += sum2 * s;
sum2 += sum1 * s;
sum1 += s;
};
for (int to : g[x]) {
if (parent[to] == x) addComponent(sub[to]);
else addComponent(n - sub[x]);
}
}
cout << ans << '\n';
}
return 0;
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
static bool isSquare(int x) {
int r = (int) sqrt((long double) x);
while ((ll)(r + 1) * (r + 1) <= x) ++r;
while ((ll)r * r > x) --r;
return (ll)r * r == x;
}
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
vector<int> a(n + 1);
for (int i = 1; i <= n; i++) cin >> a[i];
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> parent(n + 1, 0), order;
order.reserve(n);
stack<int> st;
st.push(1);
parent[1] = -1;
while (!st.empty()) {
int v = st.top();
st.pop();
order.push_back(v);
for (int to : g[v]) {
if (to == parent[v]) continue;
parent[to] = v;
st.push(to);
}
}
vector<int> sub(n + 1, 1);
for (int i = n - 1; i >= 0; i--) {
int v = order[i];
for (int to : g[v]) {
if (parent[to] == v) sub[v] += sub[to];
}
}
ll ans = 0;
for (int x = 1; x <= n; x++) {
if (!isSquare(a[x])) continue;
ll sum1 = 0;
ll sum2 = 0;
auto addComponent = [&](ll s) {
ans += sum1 * s;
ans += sum2 * s;
sum2 += sum1 * s;
sum1 += s;
};
for (int to : g[x]) {
if (parent[to] == x) addComponent(sub[to]);
else addComponent(n - sub[x]);
}
}
cout << ans << '\n';
}
return 0;
}