zoukankan      html  css  js  c++  java
  • bzoj 3611: [Heoi2014]大工程

    Description

    国家有一个大工程,要给一个非常大的交通网络里建一些新的通道。 
    我们这个国家位置非常特殊,可以看成是一个单位边权的树,城市位于顶点上。 
    在 2 个国家 a,b 之间建一条新通道需要的代价为树上 a,b 的最短路径。
     现在国家有很多个计划,每个计划都是这样,我们选中了 k 个点,然后在它们两两之间 新建 C(k,2)条 新通道。
    现在对于每个计划,我们想知道:
     1.这些新通道的代价和
     2.这些新通道中代价最小的是多少 
    3.这些新通道中代价最大的是多少
     

    Input

    第一行 n 表示点数。

     接下来 n-1 行,每行两个数 a,b 表示 a 和 b 之间有一条边。
    点从 1 开始标号。 接下来一行 q 表示计划数。
    对每个计划有 2 行,第一行 k 表示这个计划选中了几个点。
     第二行用空格隔开的 k 个互不相同的数表示选了哪 k 个点。
     

    Output

    输出 q 行,每行三个数分别表示代价和,最小代价,最大代价。 

     

    Sample Input

    10
    2 1
    3 2
    4 1
    5 2
    6 4
    7 5
    8 6
    9 7
    10 9
    5
    2
    5 4
    2
    10 4
    2
    5 2
    2
    6 1
    2
    6 1

    Sample Output

    3 3 3
    6 6 6
    1 1 1
    2 2 2
    2 2 2

    HINT

    n<=1000000 

    q<=50000并且保证所有k之和<=2*n 

    Source

    题意:询问树上关键点两两间距离的和以及两两间距离的最大值和最小值;
     
    首先讲一下虚树的构建过程,虚树是只包含关键点和关键点的Lca的一颗树。
    具体构建方法是按照dfs序从小往大插入,然后用栈维护右链,栈顶元素表示当前栈中最后一个插入到虚树上的点。。。
    我们考虑当前点a[i],栈顶元素zhan[tp]以及前两者的Lca(记为p)的关系。。
     
    1.p=zhan[tp],那么a[i]在zhan[tp]的子树内,而且他们的路径间的点不会成为其余关键点的Lca。。。
    那么把他们相连,弹栈,再把a[i]压入栈中,也就是插入这一条右链。。。
     
    2.如果不是上一种情况,那么zhan[tp]和a[i]分居与p的两棵子树中,而且zhan[tp]内的关系已经处理完了,a[i]成为了新右链的开端,我们需要处理zhan[tp]到p的这一段。。。
    那么zhan[tp]到Lca这一段上的点也显然不可能成为其余关键点间的Lca,那么可可以一直弹栈,并把zhan[tp]和zhan[tp-1]相连。。。一直弹到deep[zhan[tp-1]]<=deep[p];
     
    1.如果deep[zhan[tp-1]]==deep[p],那么p和zhan[tp-1]是同一个点,而且弹到这一步的时候,p的含最开始的那个zhan[tp]的子树已经全部处理完毕了,break;
     
    2.如果deep[zhan[tp-1]<deep[p],那么p到zhan[tp-1]这一段是有可能成为其余关键点的Lca的,那么这一段不能直接相连,
    那么我们把p压入栈中,表示先要处理完p的子树内才能处理zhan[tp-1],但是原来的那棵子树还是处理完了,break。。。
    最后我们再把尚在栈中的右链一直弹栈,最后保留的栈顶元素就是虚树的根。。。
     
    然后讲一下dp,第一问的话就是单独考虑每条边会被经过多少次,跟HAOI2015很像。。。
    然后求树的直径的话,有一种基于点分思想的dp,dp[i]表示i到子树中的最长路,然后每新加入一棵子树就把两条路径组合一下,并更新dp[i]。。。
    记得特判关键点的路径也可以不组合。。。最小值类似。。。
    // MADE BY QT666
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<iostream>
    #include<cstring>
    #define int long long
    using namespace std;
    typedef long long ll;
    const int N=2000050;
    const int Inf=19260817;
    struct data{
      int head[N],to[N],nxt[N],v[N],cnt;
      void lnk(int x,int y){
        if(x==y) return;
        to[++cnt]=y,nxt[cnt]=head[x],head[x]=cnt;
        to[++cnt]=x,nxt[cnt]=head[y],head[y]=cnt;
      }
    }g,g2;
    int size[N],dfn[N],tt,son[N],top[N],fa[N],deep[N],n,q;
    int a[N],zhan[N],k,sz[N],dp[N],f1[N],f2[N],bj[N],ans1,ans2;
    bool cmp(int a,int b){return dfn[a]<dfn[b];}
    void dfs1(int x,int f){
      size[x]=1;
      for(int i=g.head[x];i;i=g.nxt[i]){
        int y=g.to[i];if(y==f) continue;
        deep[y]=deep[x]+1;fa[y]=x;dfs1(y,x);
        size[x]+=size[y];if(size[y]>size[son[x]]) son[x]=y;
      }
    }
    void dfs2(int x,int f){
      top[x]=f;dfn[x]=++tt;
      if(son[x]) dfs2(son[x],f);
      for(int i=g.head[x];i;i=g.nxt[i]){
        int y=g.to[i];if(y==fa[x]||y==son[x]) continue;
        dfs2(y,y);
      }
    }
    int Lca(int x,int y){
      while(top[x]!=top[y]){
        if(deep[top[x]]<deep[top[y]]) swap(x,y);
        x=fa[top[x]];
      }
      if(deep[x]<deep[y]) swap(x,y);
      return y;
    }
    void dfs(int x,int f){
      sz[x]=bj[x];f1[x]=Inf,f2[x]=0;dp[x]=0;
      for(int i=g2.head[x];i;i=g2.nxt[i]){
        int y=g2.to[i],v=deep[y]-deep[x];if(y==f) continue;
        dfs(y,x);dp[x]+=dp[y]+v*(sz[y])*(k-sz[y]);
        ans1=min(ans1,f1[x]+f1[y]+v);f1[x]=min(f1[x],f1[y]+v);
        ans2=max(ans2,f2[x]+f2[y]+v);f2[x]=max(f2[x],f2[y]+v);
        sz[x]+=sz[y];
      }
      if(bj[x]){ans1=min(ans1,f1[x]),ans2=max(ans2,f2[x]),f1[x]=0;}
      g2.head[x]=0;bj[x]=0;
    }
    void solve(){
      ans1=Inf,ans2=0;
      scanf("%lld",&k);for(int i=1;i<=k;i++) scanf("%lld",&a[i]),bj[a[i]]=1;
      sort(a+1,a+1+k,cmp);int tp=0;g2.cnt=0;
      for(int i=1;i<=k;i++){
        if(!tp) {zhan[++tp]=a[i];continue;}
        int p=Lca(zhan[tp],a[i]);
        while(1){
          if(deep[zhan[tp-1]]<=deep[p]){
        g2.lnk(zhan[tp],p);tp--;
        if(zhan[tp]!=p) zhan[++tp]=p;
        break;
          }
          g2.lnk(zhan[tp],zhan[tp-1]);tp--;
        }
        zhan[++tp]=a[i];
      }
      while(tp>1) g2.lnk(zhan[tp],zhan[tp-1]),tp--;
      dfs(zhan[tp],zhan[tp]);
      printf("%lld %lld %lld
    ",dp[zhan[tp]],ans1,ans2);
    }
    main(){
      scanf("%lld",&n);
      for(int i=1;i<n;i++){int x,y;scanf("%lld%lld",&x,&y);g.lnk(x,y);}
      dfs1(1,0);dfs2(1,1);
      scanf("%lld",&q);while(q--) solve();
      return 0;
    }
  • 相关阅读:
    CPU 上下文切换是什么
    Linux性能优化实战
    JavaScript 概述
    最全前端资源汇总
    zabbix 源码安装
    单例
    php防止sql注入
    python 多进程读写文件
    python twisted异步将数据导入到数据库中
    scrapy-splash常用设置
  • 原文地址:https://www.cnblogs.com/qt666/p/7481862.html
Copyright © 2011-2022 走看看