孤独的树(牛客月赛)

简介: 孤独的树(牛客月赛)

小白月赛最后一题,树形dp有两种方式做,第一种思维要求高一点,第二种代码麻烦一点。

解法一:

从树形结构自下而上观察本问题(上指的是树根,下指的是叶子)。

某个节点,与其子节点有公共的最大公因数,两个节点中必须要有一个要删除他们的最大公因数,删那个的对结果的贡献都是最大公因数的质数组成数那么我们删哪一个呢?我们删父节点!
因为 删了父节点之后,因为我们是从叶子节点看起,删了父节点,这个gcd的影响就传不到父节点的父节点去了,而且还防止了删了这个子节点的gcd,别的子节点和父节点还有gcd或者这个gcd的因子

所以从叶子节点往上,我们只需要一直删父节点的gcd就行了,并且将删gcd的次数加到答案里

代码如下:十分优美

#include <bits/stdc++.h>
using namespace std;
#define x first
#define y second
# define rep(i,be,en) for(int i=be;i<=en;i++)
# define pre(i,be,en) for(int i=be;i>=en;i--)
#define ll long long
#define endl "\n"
#define LOCAL
#define pb push_back
#define int long long
typedef pair<ll, ll> PII;
#define eb emplace_back
#define sp(i) setprecision(i)
const int N = 2e5 + 10, INF = 0x3f3f3f3f;
int cnt[N]; //i 的质因子多少个
int w[N];
vector<int>g[N];
int n;
int ans=0;
void init()
{
    for(int i=2;i<N;i++)
    {
        if(!cnt[i])
        {
            for(int j=i;j<N;j+=i)
            {
                int t=j;
                while(t%i==0) cnt[j]++,t/=i;
            }
        }
    }
}
void dfs(int u,int fa)
{
    for(auto x: g[u])
    {
        if(x==fa) continue;
        dfs(x,u);
        ans+=cnt[__gcd(w[u],w[x])];
        w[u]/=__gcd(w[u],w[x]);
    }
}
void solve()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>w[i];
    for(int i=1;i<=n-1;i++)
    {
        int x,y;
        cin>>x>>y;
        g[x].push_back(y);
        g[y].push_back(x);
    }
    dfs(1,-1);
    cout<<ans<<endl;
}
signed main() {    
  std::ios::sync_with_stdio(false);
  std::cin.tie(nullptr);
  //#ifdef LOCAL
  //freopen("data.in.txt","r",stdin);
  //freopen("data.out.txt","w",stdout);
  //#endif
  int __ = 1;init();
  //cin>>__;
  while (__--)
  {
    solve();
  }
  return 0;
}

**解法二:**维护每个最大的质因子组成的森林,用基础的区间dp来维护答案,我们用d p [ i ] [ 0 ] dp[i][0]dp[i][0]表示没有选i子树删了目前的质因子的最小次数,d p [ i ] [ 1 ] dp[i][1]dp[i][1]表示选了i子树删了目前质因子的最小次数。

状态转移为:d p [ u ] [ 1 ] = dp[u][1]=dp[u][1]=∑ m i n ( d p [ x ] [ 1 ] , d p [ x ] [ 0 ] ) + c n t \sum min(dp[x][1],dp[x][0])+cntmin(dp[x][1],dp[x][0])+cnt

d p [ u ] [ 0 ] = dp[u][0]=dp[u][0]=∑ d p [ x ] [ 1 ] \sum dp[x][1]dp[x][1]

代码如下:详情看注释

#include <bits/stdc++.h>
using namespace std;
#define x first
#define y second
# define rep(i,be,en) for(int i=be;i<=en;i++)
# define pre(i,be,en) for(int i=be;i>=en;i--)
#define ll long long
#define endl "\n"
#define LOCAL
#define pb push_back
#define int long long
typedef pair<ll, ll> PII;
#define eb emplace_back
#define sp(i) setprecision(i)
const int N = 1e5 + 10, INF = 0x3f3f3f3f;
int w[N];
int n;
int prime[N];//欧拉筛存质数
bool st[N];//注意,prime[i]的节点之间可能不是相连的而是森林,所以维护st[i]
int f[N][2];
int maxp[N];//这个数的最大的质因子
int cnt = 0;
vector<int>g[N];//存树
vector<int>rk[N];//此时存质因子的节点有那些
void init()
{
  for (int i = 2; i < N; i++)
  {
    if (!maxp[i]) prime[cnt++] = i, maxp[i] = i;//质数的最大的质因子是自己
    for (int j = 0; j < cnt && prime[j]*i < N; j++)
    {
      maxp[i * prime[j]] = prime[j];
      if (i % prime[j] == 0) break;
    }
  }
}
void dfs(int u, int fa, int p)
{
  st[u] = 1;
  f[u][0] = 0;
  int t = w[u], c = 0;
  while (t % p == 0)
  {
    c++;
    t /= p;
  }
  f[u][1] = c;
  for (auto x : g[u])
  {
    if (w[x] % p != 0 || x == fa || st[x]) continue;
    dfs(x, u, p);
    f[u][0] += f[x][1];
    f[u][1] += min(f[x][1], f[x][0]);
  }
}
void solve()
{
  cin >> n;
  init();
  for (int i = 1; i <= n; i++)
  {
    cin >> w[i];
    int t = w[i];
    while (t > 1)
    {
      rk[maxp[t]].push_back(i);//预处理
      t /= maxp[t];
    }
  }
  for (int i = 1; i < n; i++)
  {
    int x, y;
    cin >> x >> y;
    g[x].push_back(y);
    g[y].push_back(x);
  }
  int ans = 0;
  for (int i = 0; i < cnt; i++)
  {
    for (auto x : rk[prime[i]])
    {
      st[x] = 0;
      f[x][0] = 0;
      f[x][1] = 0;
    }
    for (auto x : rk[prime[i]])
    {
      if (!st[x])//注意,prime[i]的节点之间可能不是相连的而是森林,所以维护st[i]
      {
        dfs(x, -1, prime[i]);
        ans += min(f[x][0], f[x][1]);
      }
    }
  }
  cout << ans << endl;
}
signed main() {
  std::ios::sync_with_stdio(false);
  std::cin.tie(nullptr);
  //#ifdef LOCAL
  //freopen("data.in.txt","r",stdin);
  //freopen("data.out.txt","w",stdout);
  //#endif
  int __ = 1;
  //cin>>__;
  while (__--)
  {
    solve();
  }
  return 0;
}


目录
相关文章
|
9月前
|
测试技术
蓝桥 晚会节目单 (线段树)
蓝桥 晚会节目单 (线段树)
|
11月前
|
算法 测试技术
蓝桥杯2022年第十三届决赛真题-卡牌——二分法
蓝桥杯2022年第十三届决赛真题-卡牌——二分法
88 0
|
12月前
|
算法 C++ Python
【每日算法Day 87】今天我脱单了,所以大家不用做题了!
【每日算法Day 87】今天我脱单了,所以大家不用做题了!
三道好题分享
上课睡觉 - AcWing题库
58 0
|
JavaScript BI
【蓝桥杯集训·周赛】AcWing 第96场周赛
文章目录 第一题 AcWing 4876. 完美数 一、题目 1、原题链接 2、题目描述 二、解题报告 1、思路分析 2、时间复杂度 3、代码详解 第二题 AcWing 4877. 最大价值 一、题目 1、原题链接 2、题目描述 二、解题报告 1、思路分析 2、时间复杂度 3、代码详解 第三题 AcWing 4878. 维护数组 一、题目 1、原题链接 2、题目描述 二、解题报告 1、思路分析 2、时间复杂度 3、代码详解
64 0
|
存储 机器学习/深度学习 人工智能
【蓝桥杯集训·周赛】AcWing 第 95 场周赛
文章目录 第一题 AcWing 4873. 简单计算 一、题目 1、原题链接 2、题目描述 二、解题报告 1、思路分析 2、时间复杂度 3、代码详解 第二题 AcWing 4874. 约数 一、题目 1、原题链接 2、题目描述 二、解题报告 1、思路分析 2、时间复杂度 3、代码详解 第三题 AcWing 4875. 整数游戏 一、题目 1、原题链接 2、题目描述 二、解题报告 1、思路分析 2、时间复杂度 3、代码详解
78 0
|
存储 机器学习/深度学习 人工智能
【蓝桥杯集训·周赛】AcWing 第94场周赛
文章目录 第一题 AcWing 4870. 装物品 一、题目 1、原题链接 2、题目描述 二、解题报告 1、思路分析 2、时间复杂度 3、代码详解 第二题 AcWing 4871. 最早时刻 一、题目 1、原题链接 2、题目描述 二、解题报告 1、思路分析 2、时间复杂度 3、代码详解 第三题 AcWing 4872. 最短路之和 一、题目 1、原题链接 2、题目描述 二、解题报告 1、思路分析 2、时间复杂度 3、代码详解
47 0
|
存储 人工智能 算法
【蓝桥杯集训·最后一次周赛】AcWing 第 97 场周赛
文章目录 第一题 AcWing 4944. 热身计算 一、题目 1、原题链接 2、题目描述 二、解题报告 1、思路分析 2、时间复杂度 3、代码详解 第二题 AcWing 4945. 比大小 一、题目 1、原题链接 2、题目描述 二、解题报告 1、思路分析 2、时间复杂度 3、代码详解 第三题 AcWing 4946. 叶子节点 一、题目 1、原题链接 2、题目描述 二、解题报告 1、思路分析 2、时间复杂度 3、代码详解
98 0
|
机器学习/深度学习
【蓝桥杯集训·每日一题】 AcWing 3996. 涂色
文章目录 一、题目 1、原题链接 2、题目描述 二、解题报告 1、思路分析 2、时间复杂度 3、代码详解 三、知识风暴 区间DP Unique函数
89 0
|
存储 人工智能
【蓝桥杯集训·周赛】AcWing 第93场周赛
文章目录 第一题 AcWing 4867. 整除数 一、题目 1、原题链接 2、题目描述 二、解题报告 1、思路分析 2、时间复杂度 3、代码详解 第二题 AcWing 4868. 数字替换 一、题目 1、原题链接 2、题目描述 二、解题报告 1、思路分析 2、时间复杂度 3、代码详解 第三题 AcWing 4869. 异或值 一、题目 1、原题链接 2、题目描述 二、解题报告 1、思路分析 2、时间复杂度 3、代码详解
71 0