题目:

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

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

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

现在国家有很多个计划,每个计划都是这样,我们选中了 k 个点,然后在它们两两之间 新建 C(k,2)条 新通道。

现在对于每个计划,我们想知道:

1.这些新通道的代价和

2.这些新通道中代价最小的是多少

3.这些新通道中代价最大的是多少

题解:

这道题貌似是虚树的板子题.

为了这道题去学了学虚树.

因为我们发现我们只用在意两端都在关键点上的路径.

所以说实际上非关键点除了增加了一些路径的长度就没有其他的用处了.

我们可以考虑在原本的树上构造出一个只包括关键点但是赋予一定边一定的边权的与原树等价的树.

这就叫做虚树.

但是我们不能做到只保留关键点,我们仍然需要保留所有关键点之间的lca和lca之间的lca

对于虚树的构造方法有\(O(nlongn)\)的,可以去看其他的讲解,这里不再叙述.

随后我们在虚树上进行dp,考虑每一条边对答案的贡献即可.

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
x=0;char ch;bool flag = false;
while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
const int maxn = 2100010;
const int inf = 0x3f3f3f3f;
ll f[maxn];int g[maxn][2];
int ans0,ans1,num;
bool col[maxn];
int son[maxn],top[maxn],dep[maxn];
int fa[maxn],dfn[maxn],dfs_clock;
struct Graph{
struct Edge{
int to,next;
}G[maxn<<1];
int head[maxn],cnt;
void add(int u,int v){
G[++cnt].to = v;
G[cnt].next = head[u];
head[u] = cnt;
}
int siz[maxn];
#define v G[i].to
void dfs(int u){
siz[u] = 1;
for(int i = head[u];i;i=G[i].next){
if(v == fa[u]) continue;
fa[v] = u;
dep[v] = dep[u] + 1;
dfs(v);
siz[u] += siz[v];
if(siz[son[u]] < siz[v]) son[u] = v;
}
}
void dfs(int u,int tp){
top[u] = tp;dfn[u] = ++ dfs_clock;
if(son[u]) dfs(son[u],tp);
for(int i = head[u];i;i=G[i].next){
if(v == fa[u] || v == son[u]) continue;
dfs(v,v);
}
}
void dp(int u,int fa){
if(col[u]) siz[u] = 1;
else siz[u] = 0;
f[u] = 0;g[u][0] = inf;g[u][1] = 0;
for(int i = head[u];i;i=G[i].next){
if(v == fa) continue;
dp(v,u);siz[u] += siz[v];
int d = dep[v] - dep[u];
f[u] += f[v] + 1LL*d*siz[v]*(num - siz[v]);
ans0 = min(ans0,g[u][0] + d + g[v][0]);
ans1 = max(ans1,g[u][1] + d + g[v][1]);
g[u][0] = min(g[u][0],d + g[v][0]);
g[u][1] = max(g[u][1],d + g[v][1]);
}
if(col[u]){
ans0 = min(ans0,g[u][0]);
ans1 = max(ans1,g[u][1]);
g[u][0] = 0;
}
head[u] = 0;
return ;
}
#undef v }pic1,pic2;
inline int lca(int u,int v){
while(top[u] != top[v]){
if(dep[top[u]] < dep[top[v]]) swap(u,v);
u = fa[top[u]];
}return dep[u] < dep[v] ? u : v;
}
int sta[maxn],a[maxn];
inline bool cmp(const int &i,const int &j){
return dfn[i] < dfn[j];
}
int main(){
int n;read(n);
for(int i=1,u,v;i<n;++i){
read(u);read(v);
pic1.add(u,v);
pic1.add(v,u);
}
pic1.dfs(1);pic1.dfs(1,1);
int q;read(q);
while(q--){
pic2.cnt = 0;
read(num);
for(int i=1;i<=num;++i){
read(a[i]);
col[a[i]] = true;
}
sort(a+1,a+num+1,cmp);
int top = 0;
for(int i=1;i<=num;++i){
if(top == 0) sta[++top] = a[i];
else{
int lc = lca(sta[top],a[i]);
while(dfn[sta[top]] > dfn[lc]){
if(dfn[sta[top-1]] <= dfn[lc]){
pic2.add(lc,sta[top]);
if(sta[--top] != lc)sta[++top] = lc;
break;
}
pic2.add(sta[top-1],sta[top]);
-- top;
}
sta[++top] = a[i];
}
}
while(top > 1) pic2.add(sta[top-1],sta[top]),top--;
ans0 = inf;ans1 = 0;
pic2.dp(sta[1],sta[1]);
printf("%lld %d %d\n",f[sta[1]],ans0,ans1);
for(int i=1;i<=num;++i) col[a[i]] = false;
}
return 0;
}

bzoj 3611: [Heoi2014]大工程 虚树的更多相关文章

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

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

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

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

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

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

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

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

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

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

  6. bzoj 3611: [Heoi2014]大工程 && bzoj 2286: [Sdoi2011消耗战

    放波建虚树的模板. 大概是用一个栈维护根节点到当前关键点的一条链,把其他深度大于lca的都弹出去. 每次做完记得复原. 还有sort的时候一定要加cmp!!! bzoj 3611 #include&l ...

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

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

  8. bzoj 3611: [Heoi2014]大工程

    #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #d ...

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

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

随机推荐

  1. Watering Grass(贪心)

    Watering Grass n sprinklers are installed in a horizontal strip of grass l meters long and w meters ...

  2. 九度OJ 1194:八进制 (进制转换)

    时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:3521 解决:2058 题目描述: 输入一个整数,将其转换成八进制数输出. 输入: 输入包括一个整数N(0<=N<=100000 ...

  3. ThinkPHP5.0 用docker部署

    Dockerfile 文件如下: FROM hub.c.163.com/shenggen/thinkphp-docker:v0.0.1 ADD . /app RUN ["chmod" ...

  4. 查看django的安装路径

    查看django的安装路径 pip3 show django

  5. Python基础(5)_字符编码、文件处理

    一.文件读取过程: 1. 文本编辑器存取文件的原理(nodepad++,pycharm,word) 打开编辑器就打开了启动了一个进程,是在内存中的,所以在编辑器编写的内容也都是存放与内存中的,断电后数 ...

  6. Linux:分区

    Linux:分区 分区表 磁盘分区表主要有两种格式,一种是限制较多的 MBR 分区表,一种是较新且限制较少的 GPT 分区表. MBR MBR 中,第一个扇区最重要,里面有主要开机记录(Master ...

  7. socket编程详解

    http://www.cnblogs.com/skynet/archive/2010/12/12/1903949.html http://blog.csdn.net/hguisu/article/de ...

  8. Facial landmark detection - 人脸关键点检测

    Facial landmark detection  (Facial keypoints detection) OpenSourceLibrary: DLib Project Home:  http: ...

  9. c++之默认参数的函数

    默认参数,看个例子就明白了 int add(int a=5,int b=6,z=3): int main(){ add():// 全部默认 add(1,5)://第三个参数默认 add(1,2,3): ...

  10. .net序列化与反序列化——提供多次存储对象集后读取不完全解决方案

    ||问题: 文本文档读取序列化文件时只能读取第一次序列化对象或对象集,而多次序列化存到同一个文本文件中不能完全读取.最近做一个简单的学生管理系统,涉及到多次将学生对象序列化后追加存储到同一个文档中.在 ...