3611: [Heoi2014]大工程
Time Limit: 60 Sec Memory Limit: 512 MBSubmit: 1497 Solved: 637
[ Submit][ Status][ Discuss]
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
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
6 6 6
1 1 1
2 2 2
2 2 2
一道神奇的题
要用到虚树。那么虚树是什么呢?详见cysblog
建一颗虚树,我们需要开一个栈。
首先求栈顶与x的lca,若lca就是栈顶,则直接将x入栈。
否则,若lca是栈中的数,或者不在栈中,则将st[x-1]和st[x]连一条边。
大概就是这样,详见代码吧。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define ll long long
#define INF 9999999
using namespace std;
const int N=1000010;
struct node{
int x,y,next,v;
}sa[N*2];int len,first[N];
int n;
int dep[N],size[N],dfn[N],son[N],fa[N],df=1,top[N];
int h[N],st[N];
bool build[N];
void ins(int x,int y)
{
if(x==y) return;
len++;
sa[len].x=x;
sa[len].y=y;
sa[len].next=first[x];
sa[len].v=dep[y]-dep[x];
first[x]=len;
}
void dfs1(int x)
{
size[x]=1;dfn[x]=df++;son[x]=0;
for(int i=first[x];i!=-1;i=sa[i].next)
{
int y=sa[i].y;
if(y!=fa[x])
{
dep[y]=dep[x]+1;
fa[y]=x;
dfs1(y);
size[x]+=size[y];
if(size[son[x]]<size[y]) son[x]=y;
}
}
}
void dfs2(int x,int tp)
{
top[x]=tp;
if(son[x]) dfs2(son[x],tp);
for(int i=first[x];i!=-1;i=sa[i].next)
{
int y=sa[i].y;
if(y!=fa[x]&&y!=son[x]) dfs2(y,y);
}
}
int lca(int x,int y)
{
while(top[x]!=top[y])
{
if(dep[top[x]]>=dep[top[y]]) x=fa[top[x]];
else y=fa[top[y]];
}
return dep[x]<dep[y]?x:y;
}
bool cmp(int x,int y)
{
return dfn[x]<dfn[y];
}
ll ans1,sum[N];
int ans2,ans3,mx[N],mn[N];
void tree_dp(int u)
{
size[u]=build[u];
mx[u]=build[u]?0:-INF;
mn[u]=build[u]?0:INF;
sum[u]=0;
for(int i=first[u];i!=-1;i=sa[i].next){
int v=sa[i].y;
tree_dp(v);
ans1+=(sum[u]+size[u]*sa[i].v)*size[v]+sum[v]*size[u];
size[u]+=size[v];
sum[u]+=sum[v]+size[v]*sa[i].v; //printf("%d\n",mn[u]+mn[v]+sa[i].v);
ans2=min(ans2,mn[u]+mn[v]+sa[i].v);
ans3=max(ans3,mx[u]+mx[v]+sa[i].v);
mn[u]=min(mn[u],mn[v]+sa[i].v);
mx[u]=max(mx[u],mx[v]+sa[i].v);
}
first[u]=-1;
}
int tp;
void solve(){
int k;
scanf("%d",&k);
for(int i=1;i<=k;i++){
scanf("%d",&h[i]);
build[h[i]]=true;
}
sort(h+1,h+1+k,cmp);
tp=len=0;
st[++tp]=1;
for(int i=1;i<=k;i++){
int now=h[i],f=lca(now,st[tp]);
if(f==st[tp]){st[++tp]=now;continue;}
while(f==lca(now,st[tp-1])){
ins(st[tp-1],st[tp]);
tp--;f=lca(now,st[tp]);
//printf("%d %d\n",now,st[tp]);
}
ins(f,st[tp]); //printf("%d %d\n",f,st[tp]);
st[tp]=f;st[++tp]=now;
}
while(--tp)ins(st[tp],st[tp+1]);//printf("%d %d\n",st[tp],st[tp+1]);
ans1=0;ans2=INF;ans3=-INF;
tree_dp(1);
printf("%lld %d %d\n",ans1,ans2,ans3);
for(int i=1;i<=k;i++)build[h[i]]=false;
}
int main()
{
scanf("%d",&n);
len=0;memset(first,-1,sizeof(first));
for(int i=1;i<n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
ins(a,b);ins(b,a);
}
dfs1(1);//printf("!");
dfs2(1,1);
int q;
memset(first,-1,sizeof(first));
scanf("%d",&q);
while(q--) solve();
}