cf1101 D. GCD Counting(树形dp)
题意:
在树中找一条最长的链,链上所有点的点权的gcd大于1。
思路:
每个数的质因子数量是非常少的,可以认为是个位数。因为2×3×5一直乘到19就快1e7了。
\(p[u][j]\) 为编号为 \(u\) 的节点的第 \(j\) 个质因子
\(dp[u][i]\) 表示以 \(u\) 为一个端点,\(u\) 的子树中的某点为另一端点的最长链的长度,且链上所有点都有质因子 \(p[u][i]\) 。dfs暴力找父子节点有无共同质因子,记录子节点的最大值和次大值,更新答案。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 2e5 + 5;
int n;
vector<int> G[N];
vector<int> p[N]; // p[i]表示a[i]的所有质因子
int dp[N][13], ans;
void dfs(int u, int fa) {
for (int i = 0; i < (int)p[u].size(); ++i) {
dp[u][i] = 1;
}
for (int v : G[u]) if (v != fa) {
dfs(v, u);
for (int i = 0; i < (int)p[u].size(); ++i) {
for (int j = 0; j < (int)p[v].size(); ++j) {
if (p[u][i] == p[v][j]) {
ans = max(ans, dp[u][i] + dp[v][j]);
dp[u][i] = max(dp[u][i], dp[v][j] + 1);
}
}
}
}
}
int minp[N], pri[N], pcnt;
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
// 线性筛预处理每个数的最小质因子,加速分解质因数,要不然会超时
for (int i = 2; i < N; ++i) {
if (!minp[i]) minp[i] = i, pri[++pcnt] = i;
for (int j = 1; i * pri[j] < N; ++j) {
minp[i * pri[j]] = pri[j];
if (minp[i] == pri[j]) break;
}
}
cin >> n;
for (int i = 1; i <= n; ++i) {
int ai;
cin >> ai;
if (ai > 1) ans = 1; // 如果ai全是1的话答案就是0
while (ai > 1) {
int d = minp[ai];
p[i].push_back(d);
while (ai % d == 0) ai /= d;
}
}
for (int i = 1; i < n; ++i) {
int x, y;
cin >> x >> y;
G[x].push_back(y);
G[y].push_back(x);
}
dfs(1, 0);
cout << ans;
return 0;
}

浙公网安备 33010602011771号