zoukankan      html  css  js  c++  java
  • [Ahoi2008]Meet 紧急集合

    1787: [Ahoi2008]Meet 紧急集合

    Time Limit: 20 Sec  Memory Limit: 162 MB
    http://www.lydsy.com/JudgeOnline/problem.php?id=1787

    Description

    Input

    Output

    Sample Input

    6 4
    1 2
    2 3
    2 4
    4 5
    5 6
    4 5 6
    6 3 1
    2 4 4
    6 6 6

    Sample Output


    5 2
    2 5
    4 1
    6 0

    HINT

    Source

    Day1

    结论1:集合点一定在某两个点的lca上

    结论2: 3个点两两算出lca,至少有2个lca相同

    结论3:不同的那个lca(或3个都相同的lca)就是集合点,3个点到这个点的总距离最小

    证明1:如图所示,假设等待点是 3、5、10

    3个点之间的路径用蓝色标注,其余路径用橙色标注

    要证明结论1,可以证明以下几点:

    ① 集合点 选在蓝色路径上的点 一定比 选在橙色路径上的点 更优

        证明:如果集合点选在橙色路径上,即三个点可以不经过集合点到达其他点,那么选橙色路径顶端的蓝色路径上一点会更优

        例如 上图中 8号点要比13号点 更优

    ② 集合点若选的不是lca,那么集合点越靠近lca,越优。

       我们假设选的点

       证明:设点a,b,c,lca为a和b的lca,设选的点d不是lca,d往lca方向移动一点,设这一点为e

               那么由d向e的过程,会使①2个点的路径长度-1,另外1个点的路径长度+1   或者② 3个点的路径长度各-1

      例子:①在上图中选3、5、10,集合点由4向2转移   ②在上图中好像没有。。。画一个三叉树,集合点由上往下移即可

    综上可证结论1

    有了结论1,就可以做这个题了,3个lca挨着算一遍即可

    结论2关键点:点向上的路径有且只有唯一的一条

    结论3关键点:相同的那个lca一定在另一个lca的上面

    这样就可以只算那一个lca即可

    然后,剩下的难以描述(语文不好),画图意会吧...

    代码一:算3个lca && 倍增求lca  && 读入优化    结果:上图第3行

    #include<algorithm>
    #include<cstdio>
    #include<cmath>
    #define N 500001
    using namespace std;
    int n,m,id[N],cnt,fa[N][21],p,deep[N],tmp;
    int front[N],next[N*2],to[N*2],tot;
    int lca1,lca2,lca3;
    int read()
    {
        int x=0; char c=getchar();
        while(c<'0'||c>'9') c=getchar();
        while(c>='0'&&c<='9') { x=x*10+c-'0'; c=getchar();}
        return x;
    }
    void add(int x,int y)
    {
        to[++tot]=y; next[tot]=front[x]; front[x]=tot;
        to[++tot]=x; next[tot]=front[y]; front[y]=tot;
    }
    void dfs(int x)
    {
        id[x]=++cnt;
        for(int i=front[x];i;i=next[i])
         if(to[i]!=fa[x][0]) 
         {
              fa[to[i]][0]=x;
              deep[to[i]]=deep[x]+1;
              dfs(to[i]);
         }
    }
    int lca(int x,int y)
    {
        if(x==y) return x;
        if(id[x]<id[y]) swap(x,y);
        for(int i=p;i>=0;i--)
         if(id[fa[x][i]]>id[y])
          x=fa[x][i];
        return fa[x][0];
    }
    int dis(int x,int y)
    {
        int lc=lca(x,y);
        return deep[x]+deep[y]-2*deep[lc];
    }
    int main()
    {
        n=read(); m=read();
        int x,y,z;
        for(int i=1;i<n;i++)
        {
            x=read(); y=read();
            add(x,y);
        }
        dfs(1);
        p=log(n)/log(2)+1;
        for(int j=1;j<=p;j++)
         for(int i=1;i<=n;i++)
          fa[i][j]=fa[fa[i][j-1]][j-1];
        int ans1,ans2,tmp;
        while(m--)
        {
            x=read(); y=read(); z=read();
            lca1=lca(x,y); lca2=lca(x,z); lca3=lca(y,z);
            ans1=lca1; ans2=dis(x,lca1)+dis(y,lca1)+dis(z,lca1);
            tmp=dis(x,lca2)+dis(y,lca2)+dis(z,lca2);
            if(tmp<ans2) 
            {
                ans2=tmp;
                ans1=lca2;
            }
            tmp=dis(x,lca3)+dis(y,lca3)+dis(z,lca3);
            if(tmp<ans2)
            {
                ans2=tmp;
                ans1=lca3;
            }
            printf("%d %d
    ",ans1,ans2);
        }
    }
    View Code

    代码二:算1个lca && 倍增求lca   && 读入优化   结果: 上图第2行

    #include<algorithm>
    #include<cstdio>
    #include<cmath>
    #define N 500001
    using namespace std;
    int n,m,id[N],cnt,fa[N][21],p,deep[N],tmp,ans2;
    int front[N],next[N*2],to[N*2],tot;
    int lca1,lca2,lca3;
    int read()
    {
        int x=0; char c=getchar();
        while(c<'0'||c>'9') c=getchar();
        while(c>='0'&&c<='9') { x=x*10+c-'0'; c=getchar();}
        return x;
    }
    void add(int x,int y)
    {
        to[++tot]=y; next[tot]=front[x]; front[x]=tot;
        to[++tot]=x; next[tot]=front[y]; front[y]=tot;
    }
    void dfs(int x)
    {
        id[x]=++cnt;
        for(int i=front[x];i;i=next[i])
         if(to[i]!=fa[x][0]) 
         {
              fa[to[i]][0]=x;
              deep[to[i]]=deep[x]+1;
              dfs(to[i]);
         }
    }
    int lca(int x,int y)
    {
        if(x==y) return x;
        if(id[x]<id[y]) swap(x,y);
        for(int i=p;i>=0;i--)
         if(id[fa[x][i]]>id[y])
          x=fa[x][i];
        return fa[x][0];
    }
    int dis(int x,int y)
    {
        int lc=lca(x,y);
        return deep[x]+deep[y]-2*deep[lc];
    }
    int main()
    {
        n=read(); m=read();
        int x,y,z;
        for(int i=1;i<n;i++)
        {
            x=read(); y=read();
            add(x,y);
        }
        dfs(1);
        p=log(n)/log(2)+1;
        for(int j=1;j<=p;j++)
         for(int i=1;i<=n;i++)
          fa[i][j]=fa[fa[i][j-1]][j-1];
        while(m--)
        {
            x=read(); y=read(); z=read();
            lca1=lca(x,y); lca2=lca(x,z); lca3=lca(y,z);
            if(lca1==lca2) tmp=lca3;
            else if(lca1==lca3) tmp=lca2;
            else tmp=lca1;
            ans2=dis(x,tmp)+dis(y,tmp)+dis(z,tmp);
            printf("%d %d
    ",tmp,ans2);
        }
    }
    View Code

    代码三:算1个lca && 树链剖分求lca && 读入优化  结果:上图第1行

    #include<algorithm>
    #include<cstdio>
    #include<cmath>
    #define N 500001
    using namespace std;
    int n,m,tmp,ans;
    int front[N],next[N*2],to[N*2],tot;
    int lca1,lca2,lca3;
    int son[N],deep[N],bl[N],fa[N];
    int read()
    {
        int x=0; char c=getchar();
        while(c<'0'||c>'9') c=getchar();
        while(c>='0'&&c<='9') { x=x*10+c-'0'; c=getchar();}
        return x;
    }
    void add(int x,int y)
    {
        to[++tot]=y; next[tot]=front[x]; front[x]=tot;
        to[++tot]=x; next[tot]=front[y]; front[y]=tot;
    }
    void dfs1(int x)
    {
        son[x]++;
        for(int i=front[x];i;i=next[i])
         if(to[i]!=fa[x]) 
         {
              fa[to[i]]=x;
              deep[to[i]]=deep[x]+1;
              dfs1(to[i]);
              son[x]+=son[to[i]];
         }
    }
    void dfs2(int x,int top)
    {
        bl[x]=top;
        int y=0;
        for(int i=front[x];i;i=next[i])
         if(to[i]!=fa[x]&&son[to[i]]>son[y]) y=to[i];
        if(!y) return;
        dfs2(y,top);
        for(int i=front[x];i;i=next[i])
         if(to[i]!=fa[x]&&to[i]!=y) dfs2(to[i],to[i]); 
    }
    int lca(int x,int y)
    {
        while(bl[x]!=bl[y])
        {
            if(deep[bl[x]]<deep[bl[y]]) swap(x,y);
            x=fa[bl[x]];
        }
        return deep[x]<deep[y] ? x:y;
    }
    int dis(int x,int y)
    {
        int lc=lca(x,y);
        return deep[x]+deep[y]-2*deep[lc];
    }
    int main()
    {
        n=read(); m=read();
        int x,y,z;
        for(int i=1;i<n;i++)
        {
            x=read(); y=read();
            add(x,y);
        }
        dfs1(1);
        dfs2(1,1);
        while(m--)
        {
            x=read(); y=read(); z=read();
            lca1=lca(x,y); lca2=lca(x,z); lca3=lca(y,z);
            if(lca1==lca2) tmp=lca3;
            else if(lca1==lca3) tmp=lca2;
            else tmp=lca1;
            ans=dis(x,tmp)+dis(y,tmp)+dis(z,tmp);
            printf("%d %d
    ",tmp,ans);
        }
    }
    View Code

    上图第4行为 算3个lca && 倍增求lca  

  • 相关阅读:
    2018.12.29-dtoj-3626
    2018.12.28-bzoj-3784-树上的路径
    2018.12.28-bzoj-2006-[NOI2010]超级钢琴
    2018.12.28-dtoj-3648-寝室管理
    2018.12.27-dtoj-4089-line
    2018.12.27-dtoj-3151-相遇
    2018.12.25-dtoj-4086-针老师(truth) || dtoj-3657: 排列(permutation)
    不要62 hdu2089
    Kia's Calculation hdu4726
    The Moving Points hdu4717
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/6822268.html
Copyright © 2011-2022 走看看