Educational Codeforces Round 58 D. GCD Countin(分解质因子+树形dp)

题目链接

题意:
给出一棵树,每个节点有权值,g(x, y)代表x节点到y节点路径上的最大公因子,dist(x, y)代表x到y的距离,求在g(x, y) > 1的条件下,dist(x, y)的最大值

思路:
首先要明白,要使gcd(x, y) > 1,只需要二者有公共质因子就行了,所以必须要分解质因子了,而且可以知道每个数的质因子不超过7个(因为前七个素数之积已经超过1e5了)。接下来,dp[i][j]代表以i节点为根的子树中,对于i的第j个因子最长的dist(i, v),其中v是i的子树中的某个节点。整个思路就是相当于对每个因子dp,求出其中最大值就行了。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <map>

using namespace std;

typedef long long ll;
typedef pair < int, int > pii;
const int maxn = 2 * 1e5 + 100;

int n, tot, ans;
int a[maxn], head[maxn], dp[maxn][10];
vector < int > fac[maxn];
map < pair < int, int >, int > ma;

struct node
{
    int v, next;
}edge[maxn * 2];

inline void add(int u, int v)
{
    edge[tot].v = v;
    edge[tot].next = head[u];
    head[u] = tot++;
}

inline void Init()
{
    tot = 0;
    memset(head, -1, sizeof(head));
}

void dfs(int u, int father)
{
    int max1[10], max2[10];    //因为我们是要找最长的链,所以说要维护一下最长距离和次长距离,而这之和就是以节点u为根的子树中最长的链
    for(int i = 0; i < fac[u].size(); ++ i)
        dp[u][i] = 1, max1[i] = 0, max2[i] = 0;
    for(int i = head[u]; i != -1; i = edge[i].next)
    {
        int v = edge[i].v;
        if(v != father)
        {
            dfs(v, u);
            for(int j = 0; j < fac[u].size(); j ++)     //对于每一个因子都要求一下
            {
                if(a[v] % fac[u][j] == 0)          //如果这个子节点能整除u的因子
                {
                    int pos = ma[pii(fac[u][j], v)];
                    int temp = dp[v][pos];
                    if(max1[j] < temp)
                    {
                        max2[j] = max1[j];
                        max1[j] = temp;
                    }
                    else if(max2[j] < temp)
                        max2[j] = temp;
                }
            }
        }
    }
    for(int i = 0; i < fac[u].size(); ++ i)         //dp[i][j]存放的是i节点的第j个因子对应的最长距离,此处的最长距离指的是i的子节点j到i的距离,不是题目中要求的最长链
    {
        dp[u][i] = max(dp[u][i], max1[i] + 1);
        ans = max(ans, max1[i] + max2[i] + 1);      //最长距离和次长距离之和再加上该节点就是以该节点为根的时候整颗子树的最长链
    }
}

int main()
{
    //freopen("in.txt", "r", stdin);
    cin >> n;
    Init();
    for(int i = 1; i <= n; ++ i)
    {
        scanf("%d", &a[i]);
    }
    int u, v;
    for(int i = 1; i <= n - 1; ++ i)
    {
        scanf("%d%d", &u, &v);
        add(u, v);
        add(v, u);
    }
    for(int i = 1; i <= n; ++ i)
    {
        int k = a[i];
        for(int j = 2; j * j <= k; ++ j)	//分解质因子
        {
            if(k % j == 0)
            {
                fac[i].push_back(j);
                ma[pii(j, i)] = fac[i].size() - 1; //map存放的是因子j是第i个数的第几个因子
                while(k % j == 0)
                    k /= j;
            }
        }
        if(k != 1)
        {
            fac[i].push_back(k);
            ma[pii(k, i)] = fac[i].size() - 1;
        }
    }
    ans = 0;
    dfs(1, -1);
    cout << ans << endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/aqa2037299560/article/details/86470570