[HEOI2014]大工程
题目描述
国家有一个大工程,要给一个非常大的交通网络里建一些新的通道。
我们这个国家位置非常特殊,可以看成是一个单位边权的树,城市位于顶点上。
在 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 行,每行三个数分别表示代价和,最小代价,最大代价。
输入输出样例
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
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]大工程的更多相关文章
- bzoj 3611: [Heoi2014]大工程 虚树
题目: 国家有一个大工程,要给一个非常大的交通网络里建一些新的通道. 我们这个国家位置非常特殊,可以看成是一个单位边权的树,城市位于顶点上. 在 2 个国家 a,b 之间建一条新通道需要的代价为树上 ...
- luogu P4103 [HEOI2014]大工程 虚树 + 树形 DP
Description 国家有一个大工程,要给一个非常大的交通网络里建一些新的通道. 我们这个国家位置非常特殊,可以看成是一个单位边权的树,城市位于顶点上. 在 2 个国家 a,b 之间建一条新通 ...
- 洛谷P4103 [HEOI2014]大工程(虚树 树形dp)
题意 链接 Sol 虚树. 首先建出虚树,然后直接树形dp就行了. 最大最小值直接维护子树内到该节点的最大值,然后合并两棵子树的时候更新一下答案. 任意两点的路径和可以考虑每条边两边的贡献,\(d[x ...
- bzoj 3611(洛谷 4103) [Heoi2014]大工程——虚树
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3611 https://www.luogu.org/problemnew/show/P4103 ...
- BZOJ.3611.[HEOI2014]大工程(虚树 树形DP)
题目链接 要求的和.最大值.最小值好像都可以通过O(n)的树形DP做,总询问点数<=2n. 于是建虚树就可以了.具体DP见DP()函数,维护三个值sum[],mx[],mn[]. sum[]要开 ...
- BZOJ 3611 [Heoi2014]大工程 ——虚树
虚树第二题.... 同BZOJ2286 #include <map> #include <cmath> #include <queue> #include < ...
- bzoj 3611[Heoi2014]大工程 虚树+dp
题意: 给一棵树 每次选 k 个关键点,然后在它们两两之间 新建 C(k,2)条 新通道. 求: 1.这些新通道的代价和 2.这些新通道中代价最小的是多少 3.这些新通道中代价最大的是多少 分析:较常 ...
- 【HEOI2014】大工程<虚树>
虚树 我们每天都用心思索着,这究竟是为了什么呢?我想我也不知道,只是觉得如果人不思考问题就很无聊. 我觉得虚树不是什么数据结构,就是一种技巧或者工具.它能把树中\(k\)个关键点以\(O(klogk) ...
- [HEOI2014][bzoj3611] 大工程 [虚树+dp]
题面: 传送门 思路: 又是一道虚树入门级的题目,但是这道题的实际难点在于dp 首先,这道题是可以点分治做的,而且因为6s时限随便浪,所以写点分治也不是不可以 但是,dp因为$O\left(n\rig ...
- bzoj 3611 [Heoi2014]大工程(虚树+DP)
3611: [Heoi2014]大工程 Time Limit: 60 Sec Memory Limit: 512 MBSubmit: 408 Solved: 190[Submit][Status] ...
随机推荐
- 2017-2018-1 我爱学Java 第四五周 作业
<打地鼠>Android游戏--需求规格说明书 工作流程 组员分工及工作量比例 <需求规格说明书>的码云链接 总结与反思 参考资料 工作流程 小组成员预先参考蓝墨云班课第八周中 ...
- 201621123043 《Java程序设计》第1周学习总结
1. 本章学习总结 Jdk的安装: eclipse的基本使用方法 Java发展史 jdk.jre.jvm 关键词之间的联系:是整个java的核心,包括了一堆java.java基础的类库.java运行环 ...
- HTML,文字两端对齐
text-align: justify样式的意思是文字两端对齐,但是有时候你会发现这东西不起左右,比如在div标签中的文字. 解决方法:在div中放一个空的span标签,并使用下面的样式. .just ...
- OO第一次总结
第一次作业: 第一次作业的指导书发下来之后我按着上面的步骤一步一步的做了之后发现项目拉下来了,怎么开始码代码呢...然后在舍友的帮助下才知道怎么建包建类,然后对Java的语法又不是很了解,于是就先把C ...
- .NET Core/.NET之Stream简介
之前写了一篇C#装饰模式的文章提到了.NET Core的Stream, 所以这里尽量把Stream介绍全点. (都是书上的内容) .NET Core/.NET的Streams 首先需要知道, Syst ...
- Mego开发文档 - 基本保存操作
基本保存操作 在Mego中没有更改跟踪,也就是说所有的新增.更新及删除都需要开发者自行判断.Mego会最为实际的将各个数据操作提交给数据库并执行. 添加数据 using (var db = new O ...
- Docker学习笔记 - Docker Compose
一.概念 Docker Compose 用于定义运行使用多个容器的应用,可以一条命令启动应用(多个容器). 使用Docker Compose 的步骤: 定义容器 Dockerfile 定义应用的各个服 ...
- POJ-3421 X-factor Chains---求因子+递推 或 素因子+组合数学
题目链接: https://vjudge.net/problem/POJ-3421 题目大意: 给你一个数X,将X分解成1~X的因子数列,前一个数可以整数后一个数,求满足条件的最大链长以及有多少条这样 ...
- hdu3342-判断有向图中是否存在(至少)3元环或回路-拓扑排序
一:题目大意: 给你一个关系图,判断是否合法, 每个人都有师父和徒弟,可以有很多个: 不合法: 1) . 互为师徒:(有回路) 2) .你的师父是你徒弟的徒弟,或者说你的徒弟是你师父的 ...
- window下编辑了脚本文件linux报如下错误
首先vi :set ff 查看文件类型 接着 下载dos2unix root用户下yum -y install dos2unix 然后 dos2unix 文件.sh 转换格式 接着在正常启动即可