[HEOI2014][bzoj3611] 大工程 [虚树+dp]
题面:
思路:
又是一道虚树入门级的题目,但是这道题的实际难点在于dp
首先,这道题是可以点分治做的,而且因为6s时限随便浪,所以写点分治也不是不可以
但是,dp因为$O\left(n\right)$的高效率,依然最好优先考虑
对于这道题的dp,我们首先记录几个数组:
siz[u]表示以u为根(包括u)的子树中,询问点的总个数
minn[u]表示子树中最短的一条由子树根到询问点的路径,maxn[u]则是最长的
sum[u]则代表这棵子树中所有要求路径的长度总和
最小路径和最大路径的dp都比较简单了
每次进入子树dp结束以后,用minn[v]更新minn[u],然后用minn[u]+minn[v]+dis(u,v)更新答案
maxn同理
注意这里虽然是单位边权,但是建完虚树以后边的长度便不再固定是1了
sum的dp比较麻烦一点
每次子树v的dp结束以后,先把siz[u]+=siz[v],然后sum[u]+=sum[v]+siz[v]*(m-siz[v])*dis(u,v)
这里的m是本次询问的所有点数
这里相当于是把v子树中的所有路径(内部的路径)的值,加上经过(u,v)这条边的路径总数乘以dis(u,v),然后加入到了总答案中
这样每个sum都会包含每条边应该经过的次数,答案是正确的
虚树建立起来以后,直接dp就可以了。注意这道题里面不能固定以1作为起点,因为路径很可能不经过1,所以需要另一种建立虚树的方法,详见代码
Code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define inf 1e18
using namespace std;
inline ll read(){
ll re=,flag=;char ch=getchar();
while(ch>''||ch<''){
if(ch=='-') flag=-;
ch=getchar();
}
while(ch>=''&&ch<='') re=(re<<)+(re<<)+ch-'',ch=getchar();
return re*flag;
}
ll n,m,dep[],st[][],dfn[],clk;
ll sum[],minn[],maxn[],siz[];
struct graph{
ll first[],cnt;
struct edge{
ll to,next;
}a[];
graph(){
memset(first,-,sizeof(first));cnt=;
}
void init(){cnt=;}
inline void add(ll u,ll v){
if(u==v) return;
a[++cnt]=(edge){v,first[u]};first[u]=cnt;
}
}G,g;
void dfs(ll u,ll f){
ll i,v;
dep[u]=dep[f]+;dfn[u]=++clk;st[u][]=f;
for(i=G.first[u];~i;i=G.a[i].next){
v=G.a[i].to;
if(v==f) continue;
dfs(v,u);
}
}
void ST(){
ll i,j;
for(j=;j<=;j++) for(i=;i<=n;i++) st[i][j]=st[st[i][j-]][j-];
}
ll lca(ll l,ll r){
if(dep[l]>dep[r]) swap(l,r);
ll i;
for(i=;i>=;i--) if(dep[st[r][i]]>=dep[l]) r=st[r][i];
if(l==r) return l;
for(i=;i>=;i--)
if(st[l][i]!=st[r][i]){
l=st[l][i];
r=st[r][i];
}
return st[l][];
}
ll q[],s[],top,ans1,ans2,ans3;
bool vis[];
bool cmp(ll l,ll r){
return dfn[l]<dfn[r];
}
void dp(ll u){
ll i,v,dis;
sum[u]=;minn[u]=inf;maxn[u]=;siz[u]=(vis[u]);
for(i=g.first[u];~i;i=g.a[i].next){
v=g.a[i].to;g.first[u]=g.a[i].next;
dp(v);dis=dep[v]-dep[u];
ans2=min(ans2,minn[u]+minn[v]+dis);minn[u]=min(minn[u],minn[v]+dis);
ans3=max(ans3,maxn[u]+maxn[v]+dis);maxn[u]=max(maxn[u],maxn[v]+dis);
siz[u]+=siz[v];
sum[u]+=sum[v]+siz[v]*(m-siz[v])*dis;
}
if(vis[u]) vis[u]=,ans2=min(ans2,minn[u]),ans3=max(ans3,maxn[u]),minn[u]=;
}
void solve(){
m=read();ll i,j,grand;g.init();
for(i=;i<=m;i++) q[i]=read(),vis[q[i]]=;
sort(q+,q+m+,cmp);top=;
for(i=;i<=m;i++){
if(!top){s[++top]=q[i];continue;}//因为现在没有一个1号结点压在栈底不可能弹出,所以需要检测栈是否为空
grand=lca(q[i],s[top]);
while(){
if(dep[s[top-]]<=dep[grand]){
g.add(grand,s[top--]);
if(s[top]!=grand) s[++top]=grand;
break;
}
g.add(s[top-],s[top]);top--;
}
if(s[top]!=q[i]) s[++top]=q[i];
}
while(--top>=) g.add(s[top],s[top+]);
ans1=ans3=;ans2=inf;
dp(s[]);//这里从栈中最后剩下的元素开始dp,因为它就是整棵虚树的根了
printf("%lld %lld %lld\n",sum[s[]],ans2,ans3);
return;
}
int main(){
ll i,j,t1,t2;n=read();
for(i=;i<n;i++){
t1=read(),t2=read();
G.add(t1,t2);G.add(t2,t1);
}
dfs(,);ST();
ll q=read();
for(i=;i<=q;i++) solve();
}
[HEOI2014][bzoj3611] 大工程 [虚树+dp]的更多相关文章
- 【HEOI2014】大工程<虚树>
虚树 我们每天都用心思索着,这究竟是为了什么呢?我想我也不知道,只是觉得如果人不思考问题就很无聊. 我觉得虚树不是什么数据结构,就是一种技巧或者工具.它能把树中\(k\)个关键点以\(O(klogk) ...
- bzoj 3611[Heoi2014]大工程 虚树+dp
题意: 给一棵树 每次选 k 个关键点,然后在它们两两之间 新建 C(k,2)条 新通道. 求: 1.这些新通道的代价和 2.这些新通道中代价最小的是多少 3.这些新通道中代价最大的是多少 分析:较常 ...
- luogu P4103 [HEOI2014]大工程 虚树 + 树形 DP
Description 国家有一个大工程,要给一个非常大的交通网络里建一些新的通道. 我们这个国家位置非常特殊,可以看成是一个单位边权的树,城市位于顶点上. 在 2 个国家 a,b 之间建一条新通 ...
- 洛谷P4103 [HEOI2014]大工程(虚树 树形dp)
题意 链接 Sol 虚树. 首先建出虚树,然后直接树形dp就行了. 最大最小值直接维护子树内到该节点的最大值,然后合并两棵子树的时候更新一下答案. 任意两点的路径和可以考虑每条边两边的贡献,\(d[x ...
- BZOJ.3611.[HEOI2014]大工程(虚树 树形DP)
题目链接 要求的和.最大值.最小值好像都可以通过O(n)的树形DP做,总询问点数<=2n. 于是建虚树就可以了.具体DP见DP()函数,维护三个值sum[],mx[],mn[]. sum[]要开 ...
- bzoj 3611: [Heoi2014]大工程 虚树
题目: 国家有一个大工程,要给一个非常大的交通网络里建一些新的通道. 我们这个国家位置非常特殊,可以看成是一个单位边权的树,城市位于顶点上. 在 2 个国家 a,b 之间建一条新通道需要的代价为树上 ...
- bzoj 3611(洛谷 4103) [Heoi2014]大工程——虚树
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3611 https://www.luogu.org/problemnew/show/P4103 ...
- BZOJ 3611 [Heoi2014]大工程 ——虚树
虚树第二题.... 同BZOJ2286 #include <map> #include <cmath> #include <queue> #include < ...
- bzoj 3572世界树 虚树+dp
题目大意: 给一棵树,每次给出一些关键点,对于树上每个点,被离它最近的关键点(距离相同被标号最小的)控制 求每个关键点控制多少个点 分析: 虚树+dp dp过程如下: 第一次dp,递归求出每个点子树中 ...
随机推荐
- UIButton 加载网络图片
以后就可以 用这个分类 UIButton轻松加载网络图片了, UIButton+WebCache.h #import <UIKit/UIKit.h> @interface UIButt ...
- SyntaxHighlighter使用方法
原名:SyntaxHighlighter,是一款用于web页面的代码着色工具,可以用来着色多种语言,可以是HTML,CSS,Javascript,还可以是C,JAVA等编程语言.最早见于Yahoo的Y ...
- python-的多线程处理
书到用时方恨少,这句话在软件杯真的深深体会到了.但是对自己对于代码的领会能力还是有自信的,在做项目的时候用到了多线程的处理,开始都不知道该怎么去百度搜索关键词
- Mybatis 插入一条或批量插入 返回带有自增长主键记录
首先讲一下, 插入一条记录返回主键的 Mybatis 版本要求低点,而批量插入返回带主键的 需要升级到3.3.1版本,3.3.0之前的都不行, <dependency> <grou ...
- vs对某些网络错误的拦截
在编写代码的过程中发现如果在写好网页中的文本框内写入js代码(以<script>1</script>输入为例) vs会自动拦截并报错,如图(密码中我也输入了<script ...
- JavaScript日期加减
JS中的日期加减使用以下方式: varcurrentDate = new Date(); 对日期加减: date.setDate(date.getDate()+n); 对月加减: date.setMo ...
- docker-compose 使用
Docker提供一个容器编排工具------>Docker Compose,它允许用户在一个模板(YAML格式)中定义一组相关联的应用容器,这组容器会根据配置模板中的"--link&q ...
- Firebase Cloud Function 编写与部署
1.设置和初始化 Firebase SDK for Cloud Functions (1).Cloud Functions 运行的是 Node v6.14.0,因此需要安装nodejs: https: ...
- 基于django的个人博客网站建立(二)
基于django的个人博客网站建立(二) 前言 网站效果可点击这里访问 今天主要完成后台管理员登录的状态以及关于文章在后台的处理 具体内容 首先接上一次内容,昨天只是完成了一个登录的跳转,其他信息并没 ...
- [Python]有关pygame库中的flip和update的区别
pygame.display.flip()和pygame.display.update()的用法上的区别: 资料一. 资料二. (资料最后更新时间:2019年1月9日)