zoukankan      html  css  js  c++  java
  • 2020牛客暑期多校训练营(第一场) B Infinite Tree

    思路:
    1、首先假设这棵树的结果已经确定,我们统计每颗子树的 w[i] 的和 val,显然 val[1] = (sum_{i=1}^n w[i]) , 假设我们当前选定点 u ,处于点 u 时的结果为 f[u], 点 u 的深度为 depeth[u],
    接下来遍历 u 的子节点 v,由于已知 f[u], 所以 f[v] = f[u] + (val[1] - 2 * val[v]) * (depeth[v] - depeth[u])。若 f[v] < f[u], 则向 v 转移, 直到不能再转移就是正确结果了。

    2、但我们不能直接这么做,因为 m = 1e5, 我们存不下来这么节点, 但是可以发现,有用的点只有 1e5 个给定点和他们的公共祖先 LCA, 所以用虚树来减少节点的数量。
    要建立虚树我们需要知道每个关键点的 dfs 序 dfn[i],depeth[i], 和 dfs序相邻两点的lca, 但在这题中,点 dfn[i * i] 一定小于 dfn[(i + 1)* (i + 1)]
    原因:假设一个数唯一分解后的结果为 (x_1^{p_1}x_2^{p_2}x_3^{p_3}x_n^{p_n}...),那么该节点的深度为 ((sum_{i = 1}^n p_i) + 1), 根节点 1 的深度为 1。
    接着看 x! 和 (x + 1)! LCA 的深度, 若 (x + 1) 的最大素因子大于 x 的最大素因子,则 x! 和 (x + 1)! 的 LCA 为 1, 否则,
    假设 x! 唯一分解后的素因子集合为 {5, 5, 5, 3, 3, 2}, (x + 1)! 唯一分解后的素因子集合为{5, 5, 5, 3, 3, 2} + {3, 3, 2},
    根据这颗树的性质,我们很容易得出 x! 和 (x + 1)! LCA 的深度为 6 (3个5, 两个3, 再加上一个根节点), 可以手画一颗树来,很容易发现这个性质。
    3、然后建立虚树再dp就好了。

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 2e5 + 50;
    int INF = 1e9 + 7;
    typedef long long LL;
    int mindiv[maxn], vis[maxn];
    int n;
    LL w[maxn];
    void init(){ // 求mindiv[i]
        for(int i = 2; i < maxn; i++){
            mindiv[i] = INF;
        }
        for(int i = 2; i < maxn; i++){
            if(vis[i]) continue;
            for(int j = 1; 1LL * j * i < maxn; j++){
                mindiv[1LL * i * j] = min(mindiv[i * j], i);
                vis[1LL * i * j] = 1;
            }
        }
    }
    
    int tree[maxn];
    int lowbit(int x){ // 通过树状数组来对因子的个数计数
        return x & (-x);
    }
    void Update(int i, int k){
        while(i <= n){
            tree[i] += k;
            i += lowbit(i);
        }
    }
    int getSum(int i){
        int res = 0;
        while(i > 0){
            res += tree[i];
            i -= lowbit(i);
        }
        return res;
    }
    
    struct Edge
    {
        int to, next;
    } edge[maxn * 4];
    
    int k, head[maxn];
    
    void add(int a, int b){
        edge[k].to = b;
        edge[k].next = head[a];
        head[a] = k++;
    }
    int top, stk[maxn], depeth[maxn], lcadepth[maxn], tot;
    void BuildTree(){ //建立虚树
        top = 1;
        tot = n;
        stk[1] = depeth[1] = 1;
        for(int i = 2; i <= n; i++){
            int cnt = 0;
            int j;
            for(j = i; j != mindiv[j]; j /= mindiv[j]) cnt++; // 计算点 i 深度
            depeth[i] = depeth[i - 1] + cnt + 1;
            lcadepth[i] = getSum(n) - getSum(j - 1) + 1;
            for(j = i; j != 1; j /= mindiv[j]) Update(mindiv[j], 1);
        }
    
        for(int i = 2; i <= n; i++){
            if(top == 1 || lcadepth[i] == depeth[stk[top]]){
                stk[++top] = i;
                continue;
            }
            while(top > 1 && lcadepth[i] <= depeth[stk[top - 1]]){
                add(stk[top - 1], stk[top]);
                top--;
            }
            if(lcadepth[i] != stk[top]){
                depeth[++tot] = lcadepth[i]; // 要给定非 1 - n 的lca的编号
                head[tot] = -1;
                w[tot] = 0;
                add(tot, stk[top]);
                stk[top] = tot;
            }
            stk[++top] = i;
        }
    
        while(top > 1){
            add(stk[top - 1], stk[top]);
            top--;
        }
    }
    LL val[maxn];
    void dfs(int u){ // 计算所有节点的val[u]
        val[u] = 0;
        val[u] = w[u];
        for(int i = head[u]; i != -1; i = edge[i].next){
            int to = edge[i].to;
            dfs(to);
            val[u] += val[to];
        }
    }
    LL ans = 0;
    void dp(int u, LL res){
        ans = min(ans, res);
        for(int i = head[u]; i != -1; i = edge[i].next){
            int to = edge[i].to;
            LL res2 = res + 1LL * (val[1] - 1LL * 2 * val[to]) * (depeth[to] - depeth[u]);
            if(res2 < res){
                dp(to, res2);
            }
        }
    }
    int main(int argc, char const *argv[])
    {
        init();
        while(~scanf("%d", &n)){
            for(int i = 0; i <= n; i++){
                head[i] = -1;
            }
            ans = 0;
            k = 0;
            for(int i = 1; i <= tot * 2; i++) {
                tree[i] = depeth[i] = lcadepth[i] = 0;
            }
            BuildTree();
            for(int i = 1; i <= n; i++){
                scanf("%lld", &w[i]);
            }
            ans = 0;
            dfs(1);
            for(int i = 1; i <= n; i++) {
                ans += 1LL * (depeth[i] - 1) * w[i];
            }
            dp(1, ans);
            printf("%lld
    ", ans);
        }
        return 0;
    }
    
  • 相关阅读:
    springboot自动装配原理
    @Inherited 注解的作用
    基础知识点:链表反转
    基础知识点:二叉树的层次遍历
    算法题:8、二进制中1的个数
    微服务_GPRC
    扎实基础_数据结构
    .net core3.0程序发布到linux+docker
    c#_安全漏洞总结
    IIS Express(电脑无管理员权限如何启用VS调试)
  • 原文地址:https://www.cnblogs.com/PCCCCC/p/13306767.html
Copyright © 2011-2022 走看看