题面:

传送门

思路:

又是一道虚树入门级的题目,但是这道题的实际难点在于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]的更多相关文章

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  9. bzoj 3572世界树 虚树+dp

    题目大意: 给一棵树,每次给出一些关键点,对于树上每个点,被离它最近的关键点(距离相同被标号最小的)控制 求每个关键点控制多少个点 分析: 虚树+dp dp过程如下: 第一次dp,递归求出每个点子树中 ...

随机推荐

  1. 漫谈 Clustering (3): Gaussian Mixture Model

    上一次我们谈到了用 k-means 进行聚类的方法,这次我们来说一下另一个很流行的算法:Gaussian Mixture Model (GMM).事实上,GMM 和 k-means 很像,不过 GMM ...

  2. bootstrap3 文档随看

    唉 昨天看的是2,早知道就只可以看3啦,虽然整体不变,但是小改小闹的还是很多啦.产品上线是需要升级的,但是像这么改会很烦哎,有些样式名字修改,用法修改,功能修改,那让用惯了2的人还得把之前记忆清除了然 ...

  3. Express中间件简单的实现原理

    上一篇理解Express的使用之后, 再总结一篇Express中间件的简单实现原理. 我们知道Express中间件就是一个个的函数, 那么怎么让这些函数有序的执行呢? 那就需要我们调用 next 函数 ...

  4. LGTB 学分块

    总时间限制:  10000ms 单个测试点时间限制:  1000ms 内存限制:  65536kB 描述 LGTB 最近在学分块,但是他太菜了,分的块数量太多他就混乱了,所以只能分成 3 块 今天他得 ...

  5. Java 数值计算精度问题

    最近刚好做到涉及金额方面的项目,不像普通in,double,floatt类型来修饰,而是用BigDecimal来修饰,就去收集下了这方面的资料,整理如下: 1.float和double只能用来做科学计 ...

  6. mysql零散操作

    添加对外用户 CREATE USER 'admin'@'%' IDENTIFIED BY '!QAZ2wsx'; GRANT ALL PRIVILEGES ON *.* TO 'admin'@'%'; ...

  7. Ubuntu设置代理上网

    代理服务器(Proxy Server)是个人网络和Internet服务商之间的中间代理机构,它负责转发合法的网络信息,对转发进行控制和登记.代理服务器作为连接Internet(广域网)与Intrane ...

  8. SSM(Spring+Spring MVC+Mybatis)开发前台后功能完整的java开源博客管理系统

    项目描述 本项目通过SSM(SpringMVC+Mybatis+Spring)框架编写的一个人博客管理系统,使用hexo主题,以及MAVEN进行对项目管理,并且前端具有粒子和点击爱心效果.后端的页面框 ...

  9. Poj3061Subsequence

    A sequence of N positive integers (10 < N < 100 000), each of them less than or equal 10000, a ...

  10. 洛谷P1553数字反转升级版

    题目链接:https://www.luogu.org/problemnew/show/P1553