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;
}

posted @ 2022-01-01 21:15  Bellala  阅读(85)  评论(0)    收藏  举报