数字转换 —— 数论+ 树形DP

13 篇文章 0 订阅

题面

题目

在这里插入图片描述

思路

一个数的约数之和是固定的,而这个约数之和可能是多个数的约数和;

因此如果我们约数之和 → → 约数连一条边,必然可以形成树的形状(并且题目说了不能出现重复数字);


建好图后,本题就等价于找出一个森林中,直径最长的树,并求出该树的直径;

这样问题就转化成了求树的直径


接下来是关于约数和部分;

如果我们暴力的去筛每个数的因子,时间复杂度为 O ( n n ) O(n \sqrt{n}) O(nn )

我们可以用埃氏筛的思想,考虑枚举某个数能成为谁的因子;

也就是枚举倍数;

这样时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)

当然还有线性筛的方法 O ( n ) O(n) O(n),太复杂了,略…


Code

#include <iostream>
#include <cstdio>
#include <algorithm>

using namespace std;

typedef long long ll;

const int N = 5e4 + 10;

int sum[N];//sum(i)表示i的约数之和是多少

bool not_root[N];

struct Edge{
    int next,v,w;
}e[N];

int head[N],cnt,ans;

void add(int u,int v,int w){
    ++cnt;
    e[cnt].v = v;
    e[cnt].w = w;
    e[cnt].next = head[u];
    head[u] = cnt;
}
int dfs(int u){
    int d1 = 0,d2 = 0;
    for(int i = head[u];i;i=e[i].next){
        int to = e[i].v;
        int d = dfs(to) + e[i].w;
        if(d > d1) d2 = d1,d1 = d;
        else if(d > d2) d2 = d;
    }
    ans = max(ans,d1+d2);
    return d1;
}
void solve(){
    int n;
    cin >> n;
    for(int i=1;i<n;++i){
        //至少2倍,题目要求约数不包含自己
        //i*j ≤ n 防止溢出
        //j枚举的是倍数
        for(int j=2;j<=n/i;++j){
            sum[i*j] += i;
        }
    }
    //一个数的约数之和是固定的,而这个约数之和可能是多个数的约数和;
    //因此我们可以连一条约数之和-> 约数 呈现出树的形状
    //不能出现0,因此从2开始枚举
    for(int i=2;i<=n;++i){
        if(i > sum[i]){
            add(sum[i],i,1);
            not_root[i] = 1;
        }
    }
    for(int i=1;i<=n;++i){
        if(!not_root[i]){
            dfs(i);
        }
    }
    cout << ans << '\n';
}

int main(){
    std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    solve();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值