题目描述

国家有一个大工程,要给一个非常大的交通网络里建一些新的通道。

我们这个国家位置非常特殊,可以看成是一个单位边权的树,城市位于顶点上。

在 2 个国家 a,b 之间建一条新通道需要的代价为树上 a,b 的最短路径。

现在国家有很多个计划,每个计划都是这样,我们选中了 k 个点,然后在它们两两之间 新建 C(k,2)条 新通道。现在对于每个计划,我们想知道: 1.这些新通道的代价和 2.这些新通道中代价最小的是多少 3.这些新通道中代价最大的是多少

输入输出格式

输入格式:

第一行 n 表示点数。

接下来 n-1 行,每行两个数 a,b 表示 a 和 b 之间有一条边。点从 1 开始标号。

接下来一行 q 表示计划数。对每个计划有 2 行,第一行 k 表示这个计划选中了几个点。

第二行用空格隔开的 k 个互不相同的数表示选了哪 k 个点。

输出格式:

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

输入输出样例

输入样例#1: 复制

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
输出样例#1: 复制

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

说明

对于第 1,2 个点: n<=10000

对于第 3,4,5 个点: n<=100000,交通网络构成一条链

对于第 6,7 个点: n<=100000

对于第 8,9,10 个点: n<=1000000

对于所有数据, q<=50000并且保证所有k之和<=2*n

看到k的和小于2*n,于是立刻想到建虚树

建出虚树后就dp

size[x]表示x的子树中关键点数

f[x]表示x子树中路径的贡献和

Min[x]表示x子树中离x距离最小的关键点的距离

Max[x]表示最大的距离

最大值和求和很简单

最大值总是要取到叶子节点,虚树中叶子节点总是关键点

答案取当前Max[x]+Max[v]+边权w,然后Max[x]=max(Max[x],Max[v]+d)

求和就考虑一条边的贡献

一条边的贡献次数显然是size[v]*(k-size[v])

所以f[x]+=f[v]+size[v]*(k-size[v])*w

求最小值的话,Min[x]初值正无穷

如果是关键点就直接取它的子树路径最小值,否则就是它的两个儿子的子树路径最小值相加

如果是关键点,更新答案后Min[x]要清0,作为接下来的端点

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long lol;
const int N=;
struct Node
{
int next,to;
}edge[N],edge2[N];
int inf=1e9;
int dep[N],fa[N][],dfn[N],cnt,bin[],head[N],head2[N],num,ed[N];
int size[N],vis[N],Max[N],Min[N],k,M,ans1,ans2,n,Lca,a[N],s[N],top;
lol f[N];
int gi()
{
char ch=getchar();
int x=;
while (ch<''||ch>'') ch=getchar();
while (ch>=''&&ch<='')
{
x=x*+ch-'';
ch=getchar();
}
return x;
}
bool cmp(int a,int b)
{
return dfn[a]<dfn[b];
}
void add(int u,int v)
{
num++;
edge[num].next=head[u];
head[u]=num;
edge[num].to=v;
}
void add2(int u,int v)
{
if (u==v) return;
num++;
edge2[num].next=head2[u];
head2[u]=num;
edge2[num].to=v;
}
void dfs(int x,int pa)
{int i;
dep[x]=dep[pa]+;
dfn[x]=++cnt;
for (i=;bin[i]<=dep[x];i++)
fa[x][i]=fa[fa[x][i-]][i-];
for (i=head[x];i;i=edge[i].next)
{
int v=edge[i].to;
if (v==pa) continue;
fa[v][]=x;
dfs(v,x);
}
ed[x]=cnt;
}
int lca(int x,int y)
{int as,i;
if (dep[x]<dep[y]) swap(x,y);
for (i=;i>=;i--)
if (bin[i]<=dep[x]-dep[y])
x=fa[x][i];
if (x==y) return x;
for (i=;i>=;i--)
{
if (fa[x][i]!=fa[y][i])
{
x=fa[x][i];y=fa[y][i];
}
}
return fa[x][];
} void dp(int x)
{int i;
size[x]=vis[x];
Max[x]=;Min[x]=inf;f[x]=;
for (i=head2[x];i;i=edge2[i].next)
{
int v=edge2[i].to,d=dep[v]-dep[x];
dp(v);
size[x]+=size[v];
f[x]+=f[v]+1ll*size[v]*(k-size[v])*d;
ans1=min(ans1,Min[x]+Min[v]+d);Min[x]=min(Min[x],Min[v]+d);
ans2=max(ans2,Max[x]+Max[v]+d);Max[x]=max(Max[x],Max[v]+d);
}
if (vis[x]) ans1=min(ans1,Min[x]),ans2=max(ans2,Max[x]),Min[x]=;
}
int main()
{int i,u,v,j,q;
cin>>n;
bin[]=;
for (i=;i<=;i++)
bin[i]=bin[i-]*;
for (i=;i<=n-;i++)
{
scanf("%d%d",&u,&v);
add(u,v);add(v,u);
}
dfs(,);
cin>>q;
while (q--)
{
k=gi();
M=k;
num=;ans1=inf;ans2=;
for (i=;i<=k;i++)
a[i]=gi(),vis[a[i]]=;
sort(a+,a+k+,cmp);
Lca=a[];
for (i=;i<=k;i++)
if (ed[a[i-]]<dfn[a[i]])
a[++M]=lca(a[i-],a[i]),Lca=lca(Lca,a[i]);
a[++M]=Lca;
sort(a+,a+M+,cmp);
M=unique(a+,a+M+)-a-;
s[++top]=a[];
for (i=;i<=M;i++)
{
while (top&&ed[s[top]]<dfn[a[i]]) top--;
add2(s[top],a[i]);
s[++top]=a[i];
}
dp(Lca);
printf("%lld %d %d\n",f[Lca],ans1,ans2);
for (i=;i<=M;i++)
vis[a[i]]=head2[a[i]]=;
}
}

[HEOI2014]大工程的更多相关文章

  1. bzoj 3611: [Heoi2014]大工程 虚树

    题目: 国家有一个大工程,要给一个非常大的交通网络里建一些新的通道. 我们这个国家位置非常特殊,可以看成是一个单位边权的树,城市位于顶点上. 在 2 个国家 a,b 之间建一条新通道需要的代价为树上 ...

  2. luogu P4103 [HEOI2014]大工程 虚树 + 树形 DP

    Description 国家有一个大工程,要给一个非常大的交通网络里建一些新的通道.  我们这个国家位置非常特殊,可以看成是一个单位边权的树,城市位于顶点上.  在 2 个国家 a,b 之间建一条新通 ...

  3. 洛谷P4103 [HEOI2014]大工程(虚树 树形dp)

    题意 链接 Sol 虚树. 首先建出虚树,然后直接树形dp就行了. 最大最小值直接维护子树内到该节点的最大值,然后合并两棵子树的时候更新一下答案. 任意两点的路径和可以考虑每条边两边的贡献,\(d[x ...

  4. bzoj 3611(洛谷 4103) [Heoi2014]大工程——虚树

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3611 https://www.luogu.org/problemnew/show/P4103 ...

  5. BZOJ.3611.[HEOI2014]大工程(虚树 树形DP)

    题目链接 要求的和.最大值.最小值好像都可以通过O(n)的树形DP做,总询问点数<=2n. 于是建虚树就可以了.具体DP见DP()函数,维护三个值sum[],mx[],mn[]. sum[]要开 ...

  6. BZOJ 3611 [Heoi2014]大工程 ——虚树

    虚树第二题.... 同BZOJ2286 #include <map> #include <cmath> #include <queue> #include < ...

  7. bzoj 3611[Heoi2014]大工程 虚树+dp

    题意: 给一棵树 每次选 k 个关键点,然后在它们两两之间 新建 C(k,2)条 新通道. 求: 1.这些新通道的代价和 2.这些新通道中代价最小的是多少 3.这些新通道中代价最大的是多少 分析:较常 ...

  8. 【HEOI2014】大工程<虚树>

    虚树 我们每天都用心思索着,这究竟是为了什么呢?我想我也不知道,只是觉得如果人不思考问题就很无聊. 我觉得虚树不是什么数据结构,就是一种技巧或者工具.它能把树中\(k\)个关键点以\(O(klogk) ...

  9. [HEOI2014][bzoj3611] 大工程 [虚树+dp]

    题面: 传送门 思路: 又是一道虚树入门级的题目,但是这道题的实际难点在于dp 首先,这道题是可以点分治做的,而且因为6s时限随便浪,所以写点分治也不是不可以 但是,dp因为$O\left(n\rig ...

  10. bzoj 3611 [Heoi2014]大工程(虚树+DP)

    3611: [Heoi2014]大工程 Time Limit: 60 Sec  Memory Limit: 512 MBSubmit: 408  Solved: 190[Submit][Status] ...

随机推荐

  1. MySQL之数据的insert-delete-update操作

    主要是对数据的一些基本操作:增加.删除.修改

  2. Alpha第六天

    Alpha第六天 听说 031502543 周龙荣(队长) 031502615 李家鹏 031502632 伍晨薇 031502637 张柽 031502639 郑秦 1.前言 任务分配是VV.ZQ. ...

  3. 项目Beta冲刺预热

    Beta准备 1. 讨论组长是否重选的议题和结论. 经过讨论,我们认为,经过一段时间的磨合,现任组长是不需要更换的. 2. 下一阶段需要改进完善的功能. 增加关于征信的功能,贴近选题主题 美化界面,尽 ...

  4. xcode进行代码覆盖率测试

    去年写的文章,搬到cnblog 本文所述的方法只对xcode5做过测试,xcode6是否可行尚未可知. 配置编译选项 首先请参考苹果官方的文档Configuring Xcode for Code Co ...

  5. 使用 memoryview 和 struct 查看一个 GIF 图像的首部

    >>> import struct >>> fmt = '<3s3sHH' # ➊ >>> with open('filter.gif', ...

  6. 第四十三条:返回零长度的数组或者集合,而不是null

    如果一个方法的返回值类型是集合或者数组 ,如果在方法内部需要返回的集合或者数组是零长度的,也就是没有实际对象在里面, 我们也应该放回一个零长度的数组或者集合,而不是返回null.如果返回了null,客 ...

  7. HNOI 2012 永无乡

    codevs 1477 永无乡 http://codevs.cn/problem/1477/ 2012年湖南湖北省队选拔赛  时间限制: 1 s  空间限制: 128000 KB   题目描述 Des ...

  8. Java 持久化操作之 --io流与序列化

    1)File类操作文件的属性 1.File类的常用方法 1. 文件的绝对完整路径:getAbsolutePath() 文件名:getName() 文件相对路径:getPath() 文件的上一级目录:g ...

  9. maven多环境部署

    1.首先在maven的pom.xml文件中添加profiles标签,然后分别添加3个不同环境的profile, 本例中添加了dev.test.product三个环境,这个可以根据自己的需要添加和减少. ...

  10. Python-socket网络编程-Day8

    目录Day8-Python socket 11.Socket 11.1.socket和file的区别: 11.2.WEB服务应用: 21.3.更多功能 21.4.socket方法: 41.5. 服务端 ...