3611: [Heoi2014]大工程

3611: [Heoi2014]大工程

Time Limit: 60 Sec   Memory Limit: 512 MB
Submit: 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

Sample Output

3 3 3
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();
}




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值