HDU5398 GCD Tree(动态树)

本文介绍了一种基于贪心思想的最大生成树算法实现,通过动态树维护节点间的最大公约数关系来构建一棵拥有最大权值的树。适用于n个点的情况,着重讲解了素数和非素数节点的处理方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/**
题意:n个点,标号1~n,现在要生成一棵树,使树的总权值最大,相邻两个点若连了边,那么其权值就是两个节点的最大公约数

思路:考虑前i个点已经生成最大生成树,第i+1个点加进去,如果i + 1是素数,那么和1连边,否则至少要和它的最大因子(不是i+1)连边,这是由贪心思想来的,
k个点的所有数的最大公约数有,k/2, k/2-1, k/2-2....1,然后把k/2的倍数依次加边,所以i+1一定会和它的最大因子连边,剩余的因子一个一个去看能不能连边,
这个可以用动态树维护, 动态树维护的是点权,可以把一条边拆成一个中间点和两端点相连,中间点的权值就是边的权值
*/

#include<bits/stdc++.h>
typedef long long ll;
const int maxn = 1e5 + 5;
const int INF = 1e9 + 10;
using namespace std;

struct edge { int cost, u, v, edge_id; };

edge min_edge(edge e1, edge e2) {
    int c1 = e1.cost, c2 = e2.cost;
    if(c1 >= c2) return e2;
    else return e1;
}

int res_node[maxn * 4], stk = 0;
struct Link_Cut {
    struct Node {
        int rev, fa, ch[2];
        edge anse, own;
    } node[maxn * 4];
    int q[maxn], loc[maxn], cnt;
    bool isroot(int x) { return node[node[x].fa].ch[1] != x && node[node[x].fa].ch[0] != x; }
    void update(int x) {
        edge el = node[node[x].ch[0]].anse;
        edge er = node[node[x].ch[1]].anse;
        node[x].anse = min_edge(node[x].own, min_edge(el, er));
    }
    bool get(int x) { return node[node[x].fa].ch[1] == x; }
    void push_down(int x) {
        if(!node[x].rev) return ;
        node[x].rev = 0; swap(node[x].ch[0], node[x].ch[1]);
        node[node[x].ch[0]].rev ^= 1;
        node[node[x].ch[1]].rev ^= 1;
    }
    void __rotate(int x) {
        int old = node[x].fa, oldf = node[old].fa, op = get(x);
        if(!isroot(old)) node[oldf].ch[node[oldf].ch[1] == old] = x;
        node[old].ch[op] = node[x].ch[op ^ 1];
        int rt = node[x].ch[op ^ 1]; node[rt].fa = old;
        node[x].ch[op ^ 1] = old; node[old].fa = x; node[x].fa = oldf;
        update(old); update(x);
    }
    void splay(int x) {
        int tp = 1; q[1] = x;
        for(int i = x; !isroot(i); i = node[i].fa) q[++tp] = node[i].fa;
        for(int i = tp; i; i--) push_down(q[i]);
        for(int u; !isroot(x); __rotate(x)) {
            u = node[x].fa;
            if(!isroot(u)) __rotate(get(x) == get(u) ? u : x);
        }
    }
    ///连接至根节点
    void access(int x) { int t = 0; while(x) { splay(x); node[x].ch[1] = t; update(x); t = x; x = node[x].fa; } }
    ///x成为根节点
    void makeroot(int x) { access(x); splay(x); node[x].rev ^= 1; }
    void link(int x, int y) { makeroot(x); node[x].fa = y; }
    void cut(int x, int y) { makeroot(x); access(y); splay(y); if(node[y].ch[0] == x) node[x].fa = node[y].ch[0] = 0; }
    int find_root(int x) { access(x); splay(x); while(node[x].ch[0]) x = node[x].ch[0]; return x; }

    void create_node(int x, int from, int to, int v) {
        cnt = res_node[stk];
        if(x < 1e5 + 1) loc[x] = cnt;
        node[cnt].rev = 0;
        node[cnt].ch[0] = node[cnt].ch[1] = node[cnt].fa = 0;
        node[cnt].anse.cost = node[cnt].own.cost = v;
        node[cnt].anse.u = node[cnt].own.u = from;
        node[cnt].anse.v = node[cnt].own.v = to;
        node[cnt].anse.edge_id = node[cnt].own.edge_id = cnt;
    }
    void add(int x, int y, int v) {
        int nd = res_node[stk - 1]; stk--; ///点代替边权
        create_node(maxn, x, y, v);
        link(loc[x], nd);
        link(nd, loc[y]);
    }
    void init() {
        node[0].anse.cost = INF; node[0].fa = 0;
        node[0].ch[0] = node[0].ch[1] = 0;
        node[0].rev = 0; node[0].own.cost = INF;
    }

    int solve(int x, vector<int> vec, int as) {
        stk--; create_node(x, 0, 0, INF);
        if(!vec.size()) { add(x, 1, 1); return as + 1; }
        int sz = vec.size(); add(x, vec[sz - 1], vec[sz - 1]); as += vec[sz - 1];
        if(vec.size() == 1) return as;
        for(int i = sz - 2; i >= 0; i--) {
            int v = vec[i];
            makeroot(loc[v]); access(loc[x]); splay(loc[x]);
            edge now = node[loc[x]].anse;
            if(now.cost >= v) continue ;
            int cx = loc[now.u], cy = loc[now.v], mid = now.edge_id;
            cut(cx, mid); cut(mid, cy); res_node[stk++] = mid;
            add(x, v, v); as += v - now.cost;
        }
        return as;
    }

} tree;

vector<int> G[maxn];
int ans[maxn], n;
void init() {
    for(int i = 2; i < maxn; i++) {
        for(int j = 2; j * j <= i; j++) {
            if(i % j) continue;
            G[i].push_back(j);
            if(j != i / j) G[i].push_back(i / j);
        }
        sort(G[i].begin(), G[i].end());
    }
    for(int i = maxn * 4 - 2; i >= 1; i--) res_node[stk++] = i;
    ans[1] = 0; stk--; tree.init();
    tree.create_node(1, 0, 0, INF);
    for(int i = 2; i <= 1e5 + 1; i++) ans[i] = tree.solve(i, G[i], ans[i - 1]);
}

int main() {
    init();
    while(scanf("%d", &n) != EOF) printf("%d\n", ans[n]);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值