[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,递归求出每个点子树中 ...
随机推荐
- netbackup :nbu备份 Hyper-V 遇到快照错误(状态码 156)
遇到快照错误(状态码 156) 下表介绍与 NetBackup 状态码 156 有关的 Hyper-V 问题. 表:状态码 156 的可能原因 状态码 156 的原因 说明及推荐操作 NetBacku ...
- find - 递归地在层次目录中处理文件
总览 SYNOPSIS find [path...] [expression] 描述 DESCRIPTION 这个文档是GNU版本 find 命令的使用手册. find 搜索目录树上的每一个文件名,它 ...
- cf1151 B
题目连接 : https://codeforces.com/contest/1151/problem/B 可能我想法有问题,我怎么感觉B题的思路不直接想出来的,我想了一会才想出来,感觉不难,但可能有更 ...
- oracle用户创建及权限设置(转)
权限: create session create table unlimited tablespace connect resource dba 例: #sqlplus /nolog SQL> ...
- Oracle分页抽数存储过程
--outTotal是需要返回的总数,v_loginUserId是传入的登录人ID,抽取他的客户,v_CurrPage是传入的第几页,v_pageSize传入的每页数据条数. ) FROM tb_cu ...
- es6中的类及es5类的实现
目录 类的特点 类的特点 1.类只能通过new得到 在es6中类的使用只能是通过new,如果你将它作为一个函数执行,将会报错. //es6的写法 class Child { constructor() ...
- 二、C到C++的升级
C++ 的加强主要表现在:类型的加强.面向对象支持 1.C++改进 C++更强调语言的实用性,所有的变量都可以再需要使用的时候再定义,C语言中的变量都必须在作用域开始的位置定义 int c = 0; ...
- 三十、MySQL 处理重复数据
MySQL 处理重复数据 有些 MySQL 数据表中可能存在重复的记录,有些情况我们允许重复数据的存在,但有时候我们也需要删除这些重复的数据. 本章节我们将为大家介绍如何防止数据表出现重复数据及如何删 ...
- rpc - 接口返回数据结构的设计
方案一: 系统级状态 .业务级别的状态同用 code要特殊声明保留状态,如若不声明保留状态,一旦业务开发人员用到了系统级的状态,就有必要侵入的改动业务返回的code(新code与业务欲返回的code ...
- 09.VUE学习之watch监听属性变化实现类百度搜索栏功能ajax异步请求数据,返回字符串
cmd下安装axios npm install axios 安装好后,会多出node_modules文件夹 思路: 监听data里的word改变时,发送ajax异步请求数据, 把返回的数据赋值给dat ...