• Codeforces379 F. New Year Tree


    Codeforces题号:#379F

    出处: Codeforces

    主要算法:LCA+树的直径

    难度:4.4

    思路分析:

      给出q个操作,每次在一个节点上接上两个叶子。每一次询问树的直径。

      暴力做法:每一次操作暴力BFS两遍……然而……复杂度时(O(Q * 2n),爆到不知哪里去了。

      其实我们会发现,除非新加进来的两个点能够对直径产生影响,直径根本不会变。所以我们只需要考虑新加进来的点对原图的影响。

      那么如何操作呢?先假设没经过任何操作之前,直径的端点时2和3(事实上2,3,4中任意两个都可以),设直径的端点1为A,端点2为B。每加进来两个点x,y时,若dist(x,A)或dist(x,B)大于原先的直径,则用它们更新,并且将直径的另一个端点设置为x或y。

      下面来证明这样的做法的正确性:

      由于在讲树的直径的时候我们提到过,从任意一个点出发进行BFS,所能到达的最远点一定是树的一个直径之一。

      先假设新加进来的两个点x,y不存在,那么原先的树的直径就是A->B,并且一定是最长的了。设x,y的父亲节点为v。那么从v所能到达的最远的点一定是A或B。并且x或y到v的距离只有1,也只能是1。所以从x或y出发遍历所能够到达的最远的点一定也是A或B。而由于之前的直径是最长的,所以我当前的直径要比上一轮更长,只能是加了一,而这个1就是从x或y到v的距离的距离中产生的。

      所以我们选择x来更新(因为x和y其实是一样的,你不可能有一条直径是从x到y的,因为这样只能是2,而刚开始就已经是2了)。分别求出x到A与B的距离,如果x到A更新成功,则B=x;如果x到B更新成功,则A=x;事实上,它们只有一个能更新成功。

      于是现在问题就转化为了求两点之间距离了,LCA随便搞一搞就好了。这题还不用Dfs预处理,真是太可爱了……

    代码注意点:

      由于有q次操作,每次操作增加两个点,所以点的数目是(2q+4),而不是(q+4)

    Code

    /** This Program is written by QiXingZhi **/
    #include <cstdio>
    #include <queue>
    #include <cstring>
    #include <algorithm>
    #define  r  read()
    #define  Max(a,b)  (((a)>(b)) ? (a) : (b))
    #define  Min(a,b)  (((a)<(b)) ? (a) : (b))
    using namespace std;
    typedef long long ll;
    const int N = 1000100;
    const int INF = 1061109567;
    inline int read(){
        int x = 0; int w = 1; register int c = getchar();
        while(c ^ '-' && (c < '0' || c > '9')) c = getchar();
        if(c == '-') w = -1, c = getchar();
        while(c >= '0' && c <= '9') x = (x << 3) +(x << 1) + c - '0', c = getchar();
        return x * w;
    }
    vector <int> G[N];
    int dep[N],f[N][30];
    int Q,v,cur_node,A,B,cur1,cur2,ans,Tmp_Dist;
    inline void AddEdge(int u, int v){
        G[u].push_back(v);
        G[v].push_back(u);
    }
    inline void Init(){
        AddEdge(1,2), AddEdge(1,3), AddEdge(1,4);
        cur_node = 4;
        A = 2, B = 3;
        ans = 2;
        f[2][0] = f[3][0] = f[4][0] = 1;
        dep[1] = 1;
        dep[2] = dep[3] = dep[4] = 2;
    }
    inline void Update(int u, int v){
        f[v][0] = u;
        dep[v] = dep[u] + 1;
        for(int i = 1; (1<<i) <= dep[v]; ++i){
            f[v][i] = f[f[v][i-1]][i-1];
        }
    }
    inline int LCA(int _a, int _b){
        if(dep[_a] < dep[_b]){
            swap(_a, _b);
        }
        int a = _a, b = _b;
        for(int i = 25; i >= 0; --i){
            if(dep[a] - (1<<i) < dep[b]) continue;
            a = f[a][i];
        }
        if(a == b) return a;
        for(int i = 25; i >= 0; --i){
            if(f[a][i] == f[b][i]) continue;
            a = f[a][i];
            b = f[b][i];
        }
        return f[a][0];
    }
    inline int GetDist(int _a, int _b){
        int __lca = LCA(_a, _b);
        return dep[_a]-dep[__lca]+dep[_b]-dep[__lca];
    }
    int main(){
        Init();
        Q = r;
        while(Q--){
            v = r;
            AddEdge(v,++cur_node);
            Update(v,cur_node);
            AddEdge(v,++cur_node);
            Update(v,cur_node);
            cur1 = cur_node;
            Tmp_Dist = GetDist(cur1,A);
            if(Tmp_Dist > ans){
                ans = Tmp_Dist;
                B = cur1;
            }
            Tmp_Dist = GetDist(cur1,B);
            if(Tmp_Dist > ans){
                ans = Tmp_Dist;
                A = cur1;
            }
            printf("%d
    ",ans);
        }
    /*    
        for(int i = 1; i <= cur_node; ++i){
            for(int j = 0; j <= 3; ++j){
                printf("f[%d][%d] = %d
    ",i,j,f[i][j]);
            }
        }
        for(int i = 1; i <= cur_node; ++i){
            printf("dep[%d] = %d
    ",i,dep[i]);
        }
    */
        return 0;
    }
  • 相关阅读:
    开通博客开心
    Kubernetes/K8s架构师实战集训营【中、高级班】-2020
    Kubernetes/K8s CKA认证全套实训视频教程下载
    centos7安装部署docker
    模拟器genymotion的安装与配置
    工欲善其事必先利其器---Android开发环境搭建
    监控利器---Zabbix(一)
    进击python第4篇:初探模块
    fuser命令小结
    进击python第三篇:基础
  • 原文地址:https://www.cnblogs.com/qixingzhi/p/9309102.html
Copyright © 2020-2023  润新知