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\n",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;
}
posted @ 2017-09-05 20:45  qt666  阅读(152)  评论(0编辑  收藏  举报