[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,递归求出每个点子树中 ...
随机推荐
- WARNING you have Transparen Huge Pages..
redis启动警告: WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will c ...
- Bootstrap历练实例:轮播(carousel)
<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content=&q ...
- 基于supersocket、C#对JT808协议进行解析构建gps监控平台服务端
GPS监控平台.车联网.物联网系统中GPRS网络数据的并发通讯和处理解析,主要功能有socket的UDP和TCP链路建立和维持,网络数据协议包接收与解析,分发上传到其他业务规则服务器,在物联网以及位置 ...
- SpringBoot之自动配置原理
我在前面的Helloworld的程序中已经分析过一次,配置原理了: 1).SpringBoot启动的时候加载主配置类,开启了自动配置功能 @EnableAutoConfiguration 2).@En ...
- requests.exceptions.SSLError……Max retries exceeded with url错误求助!!!
import requests head = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Appl ...
- 用django实现邮件发送
settings配置 EMAIL_HOST = 'smtp.qq.com' # 如果是163换成stmp.163.com EMAIL_PORT = 465 # qq邮箱的端口 EMAIL_HOST_U ...
- Leetcode 515. 在每个树行中找最大值
题目链接 https://leetcode-cn.com/problems/find-largest-value-in-each-tree-row/description/ 题目描述 您需要在二叉树的 ...
- 配置Wampserver和安装thinksns
一.先安装Wampserver(去官网下载) 二.安装好后单击wampserver图标,Apache->Service->测试80端口,如果显示: i 端口被iis占用 控制面板-> ...
- 小米r3g旧版开发版固件,安装opkg
1.开启ssh 1.1.刷入固件 在路由器更新界面,刷入 miwifi_r3g_firmware_c2175_2.25.122.bin 固件 下载地址: http://bigota.miwifi.co ...
- Django基于Pycharm开发之四[关于静态文件的使用,配置以及源码分析](原创)
对于django静态文件的使用,如果开发过netcore程序的开发人员,可能会比较容易理解django关于静态文件访问的设计原理,个人觉得,这是一个middlerware的设计,但是在django中我 ...