BZOJ3611 [Heoi2014]大工程

3611: [Heoi2014]大工程

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

HINT

n<=1000000 


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

Source

[ Submit][ Status][ Discuss]

HOME Back

题解

建立虚树。最大最小值DP非常显然。至于求和有两种办法:

  1. 统计每条边算了多少次
  2. 维护路径长度和sum和siz,每次ans+=sum[u]*siz[v]+siz[u]*(sum[v]+w*siz[v])。

注意只有实际询问的节点统计siz就行了。无聊啊……放一下别人的代码
第一种做法的

void dfs2(int x) {
    siz[x]=bo[x],maxs[x]=0,mins[x]=inf,f[x]=0;
    for (int y=now[x]; y; y=pre[y]) {
        int d=dis[son[y]]-dis[x];
        dfs2(son[y]),siz[x]+=siz[son[y]];
        ans1=min(ans1,mins[x]+mins[son[y]]+d),mins[x]=min(mins[x],mins[son[y]]+d);
        ans2=max(ans2,maxs[x]+maxs[son[y]]+d),maxs[x]=max(maxs[x],maxs[son[y]]+d);
        f[x]+=f[son[y]]+1ll*siz[son[y]]*(num-siz[son[y]])*d;
    }
    if (bo[x]) ans1=min(ans1,mins[x]),ans2=max(ans2,maxs[x]),mins[x]=0;
    now[x]=0;
}

第二种做法的

void dp(int u){
    siz[u]=build[u];
    mx[u]=build[u]?0:-inf;
    mi[u]=build[u]?0:inf;
    sum[u]=0;
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to;
        dp(v);
        ans1+=(sum[u]+siz[u]*e[i].v)*siz[v]+sum[v]*siz[u];
        siz[u]+=siz[v];
        sum[u]+=sum[v]+siz[v]*e[i].v;
        ans2=min(ans2,mi[u]+mi[v]+e[i].v);
        ans3=max(ans3,mx[u]+mx[v]+e[i].v);
        mi[u]=min(mi[u],mi[v]+e[i].v);
        mx[u]=max(mx[u],mx[v]+e[i].v);
    }
    head[u]=0;
}

猜你喜欢

转载自www.cnblogs.com/autoint/p/11056681.html