●洛谷P3233 [HNOI2014]世界树
题链:
https://www.luogu.org/problemnew/show/P3233
题解:
虚树,dp,倍增。
首先对于每个询问,要把虚树建出来,这一步就从略了。这里着重分享一下如何求答案。
比如,我们建出来如下一颗虚树,给出的关键点是那些黑点点们,红点点是"被迫"加入的LCA

然后,我们就要去求每个黑点的答案了!
等等,YY了一会儿,发现好像现在还并不方便直接求。
那么我们一步一步来,先求出这颗虚树上的每个节点属于哪个黑点管辖(到哪个黑点最近)。
用near[u]表示距离u点最近的黑点是哪个,dis[u]表示u点到near[u]的距离
怎么做呢?
首先,赋一下初值,然后两个dp,就完了。
首先,黑点当然被自己管辖,所以near[u]=u,dis[u]=0
然后进行第一个dp,我们尝试用儿子v去更新其父亲u。
if dis[u]>dis[v]+e[u->v] then near[u]=near[v],dis[u]=dis[v]+e[u->v]
这个e[u->v]就是u到v的距离,在建虚树时得到。
然后进行第二个dp,我们尝试用父亲u去更新儿子v。
if dis[v]>dis[u]+e[u->v] then near[v]=near[u],dis[v]=dis[u]+e[u->v]
这样之后,就得到这颗虚树正确的near[]和dis[],
换句话说,我们已经知道了原树上的最最最关键的那些点究竟是属于哪个黑点管辖的。
接下来就是看如何处理其它点。
我们先把虚树画再详细一点:

棕色的点就是被虚树省略掉的不重要的点,
而现在我们需要确定的就是这些点会怎样给那些黑点贡献答案。
显然我们不能一个一个地去考虑棕色的点。
但是注意到,所有棕色的节点都是从虚树的边上长出来的,(或者从虚树上的点长出来的),
所以我们的做法是:
1)、依次考虑每条虚树的边:

我们把一条虚树的边放大,假设它长这样。
虽然边上长出去的点依旧繁多复杂,但是不难注意到,
我们需要考虑的仅仅是虚树边上的那些绿点到底是属于near[u],还是near[v],
而其它的棕色点,只需要跟随其绿点父亲就好啦。
显而易见,在那条绿色的链上,会存在一个分界点,
分界点以上的绿点和棕点,被near[u]管辖,
分界点以下的绿点和棕点,被near[v]管辖:

而那个分界点,就直接用倍增去找就好了。
然后用size[]数组的差值去直接贡献给near[u]或near[v]就好了。
2)、其它直接长在虚树点上的棕色点

对于这种点,当然直接是从哪个u长出来,就被哪个near[u]所管辖。
也只用size[]的差值就可以计算出他们的贡献。
以上就是所有的情况,在代码实现的时候,还需要三思而后行哦。
代码:
#include<bits/stdc++.h>
#define MAXN 300005
#define INF 0x3f3f3f3f
using namespace std;
int N,Q,M;
bool mark[MAXN];
int fa[MAXN][20],dfn[MAXN],deep[MAXN],size[MAXN];
int near[MAXN],dis[MAXN],ANS[MAXN];
struct Edge{
int ent;
int to[MAXN*2],val[MAXN*2],nxt[MAXN*2],head[MAXN];
Edge(){ent=2;}
void Adde(int u,int v,int w){
to[ent]=v; val[ent]=w; nxt[ent]=head[u]; head[u]=ent++;
}
}E1,E2;
bool cmp(int a,int b){return dfn[a]<dfn[b];}
void read(int &x){
static int sign; static char ch;
x=0; sign=1; ch=getchar();
for(;ch<'0'||'9'<ch;ch=getchar()) if(ch=='-') sign=-1;
for(;'0'<=ch&&ch<='9';ch=getchar()) x=x*10+ch-'0';
if(sign==-1) x=-x;
}
void dfs(int u,int dep){
static int dnt;
deep[u]=dep; size[u]=1; dfn[u]=++dnt;
for(int k=1;k<20;k++) fa[u][k]=fa[fa[u][k-1]][k-1];
for(int e=E1.head[u];e;e=E1.nxt[e]){
int v=E1.to[e]; if(v==fa[u][0]) continue;
fa[v][0]=u; dfs(v,dep+1); size[u]+=size[v];
}
}
int LCA(int u,int v){
if(deep[u]>deep[v]) swap(u,v);
for(int k=19;k>=0;k--) if(deep[fa[v][k]]>=deep[u]) v=fa[v][k];
if(u==v) return u;
for(int k=19;k>=0;k--) if(fa[u][k]!=fa[v][k]) u=fa[u][k],v=fa[v][k];
return fa[u][0];
}
void dp1(int u){
if(mark[u]) near[u]=u,dis[u]=0;
for(int e=E2.head[u];e;e=E2.nxt[e]){
int v=E2.to[e]; dp1(v);
if(dis[u]<dis[v]+E2.val[e]) continue;
if(dis[u]==dis[v]+E2.val[e]&&near[u]<near[v]) continue;
near[u]=near[v]; dis[u]=dis[v]+E2.val[e];
}
}
void dp2(int u){
for(int e=E2.head[u];e;e=E2.nxt[e]){
int v=E2.to[e];
if(dis[v]>dis[u]+E2.val[e]||(dis[v]==dis[u]+E2.val[e]&&near[v]>near[u]))
near[v]=near[u],dis[v]=dis[u]+E2.val[e];
dp2(v);
}
}
int findson(int v,int u){
for(int k=19;k>=0;k--)
if(deep[fa[v][k]]>deep[u]) v=fa[v][k];
return v;
}
int search(int v,int dv,int u,int du){
static int nearv,nearu,distov,distou;
nearv=near[v]; nearu=near[u];
for(int k=19;k>=0;k--) if(deep[fa[v][k]]>deep[u]){
distov=dv+deep[v]-deep[fa[v][k]];
distou=du+deep[fa[v][k]]-deep[u];
if(distov<distou||(distov==distou&&nearv<nearu))
dv=distov,v=fa[v][k];
}
return v;
}
void dp3(int u){
int sum=0,x,y;
for(int e=E2.head[u];e;e=E2.nxt[e]){
int v=E2.to[e]; x=findson(v,u); sum+=size[x];
if(near[v]==near[u]) ANS[near[u]]+=size[x]-size[v];
else{
y=search(v,dis[v],u,dis[u]);
ANS[near[u]]+=size[x]-size[y];
ANS[near[v]]+=size[y]-size[v];
}
dp3(v);
}
ANS[near[u]]+=size[u]-sum;
//reset these arrays
E2.head[u]=0; near[u]=0; dis[u]=INF;
}
void solve(){
static int a[MAXN],b[MAXN],stk[MAXN],top,lca;
read(M); top=0; E2.ent=2;
for(int i=1;i<=M;i++) read(a[i]),mark[a[i]]=1,b[i]=a[i];
sort(a+1,a+M+1,cmp); stk[++top]=1;
for(int i=1;i<=M;i++){
lca=LCA(stk[top],a[i]);
if(lca!=stk[top]) while(1){
if(dfn[stk[top-1]]<=dfn[lca]){
E2.Adde(lca,stk[top],deep[stk[top]]-deep[lca]),top--;
if(stk[top]!=lca) stk[++top]=lca;
break;
}
E2.Adde(stk[top-1],stk[top],deep[stk[top]]-deep[stk[top-1]]),top--;
}
if(stk[top]!=a[i]) stk[++top]=a[i];
}
while(top>1) E2.Adde(stk[top-1],stk[top],deep[stk[top]]-deep[stk[top-1]]),top--;
dp1(1); dp2(1); //Get the nearest key vertex of each vertex on the virtual tree.
dp3(1); //Get the answers.
for(int i=1;i<=M;i++){
printf("%d%c",ANS[b[i]],i==M?'\n':' ');
ANS[b[i]]=0; mark[b[i]]=0;
}
}
int main(){
read(N);
memset(dis,0x3f,sizeof(dis));
for(int i=1,a,b;i<N;i++){
read(a); read(b);
E1.Adde(a,b,1); E1.Adde(b,a,1);
}
dfs(1,1); read(Q);
while(Q--) solve();
return 0;
}
●洛谷P3233 [HNOI2014]世界树的更多相关文章
- 洛谷P3233 [HNOI2014]世界树
虚树= = #include<cstdio> #include<cstdlib> #include<algorithm> #include<cstring&g ...
- 洛谷 P3233 [HNOI2014]世界树(虚树+dp)
题面 luogu 题解 数据范围已经告诉我们是虚树了,考虑如何在虚树上面\(dp\) 以下摘自hzwer博客: 构建虚树以后两遍dp处理出虚树上每个点最近的议事处 然后枚举虚树上每一条边,考虑其对两端 ...
- 洛谷P3233 世界树 [HNOI2014] 虚树
正解:虚树 解题报告: 传送门! 首先看到这种就要想到虚树这个是毫无疑问的QwQ 建虚树什么的都可以循规蹈矩地做,不说辣,具体可以看下虚树学习笔记什么的看下板子 但是建好虚树之后怎么搞还是有点儿讲究, ...
- 洛谷P3233 世界树
题意:给定树上k个关键点,每个点属于离他最近,然后编号最小的关键点.求每个关键点管辖多少点. 解:虚树 + DP. 虚树不解释.主要是DP.用二元组存虚树上每个点的归属和距离.这一部分是二次扫描与换根 ...
- 洛谷 P3237 [HNOI2014]米特运输 解题报告
P3237 [HNOI2014]米特运输 题目描述 米特是\(D\)星球上一种非常神秘的物质,蕴含着巨大的能量.在以米特为主要能源的D星上,这种米特能源的运输和储存一直是一个大问题. \(D\)星上有 ...
- 洛谷 P3235 [HNOI2014]江南乐 解题报告
P3235 [HNOI2014]江南乐 Description 两人进行 T 轮游戏,给定参数 F ,每轮给出 N 堆石子,先手和后手轮流选择石子数大于等于 F 的一堆,将其分成任意(大于1)堆,使得 ...
- 洛谷P3237 [HNOI2014]米特运输(树形dp)
解题报告 题干 米特是D星球上一种非常神秘的物质,蕴含着巨大的能量.在以米特为主要能源的D星上,这种米特能源的运输和储存一直是一个大问题. D星上有N个城市,我们将其顺序编号为1到N,1号城市为首都. ...
- 洛谷P3235 [HNOI2014]江南乐(Multi-SG)
题目描述 小A是一个名副其实的狂热的回合制游戏玩家.在获得了许多回合制游戏的世界级奖项之后,小A有一天突然想起了他小时候在江南玩过的一个回合制游戏. 游戏的规则是这样的,首先给定一个数F,然后游戏系统 ...
- luogu P3233 [HNOI2014]世界树
传送门 我是什么时候写的这题的qwq 首先,发现关键点的总数被限制了,很自然想到虚树,并且,对于一个关键点,他管理的点显然是一个联通块 然后把虚树先建出来,然后两次dfs,第一次是向祖先更新离每个点最 ...
随机推荐
- 4c语言的第0次作业
1.你认为大学的学习生活.同学关系.师生关系应该是怎样? 我认为大学的学习生活应该是充实有意义的,有对学习的激情又有与伙伴相知的愉悦. 我认为同学关系应该是互相尊重,互相学习,坦诚相待. 我认为师生关 ...
- c语言字符类型作业
一.PTA实验作业 题目1:7-2 统计一行文本的单词个数 1. 本题PTA提交列表 2. 设计思路 1.定义整形变量i=0,count=0,flag. 2.定义数组str[999] 3.输入str[ ...
- 如何使用ILAsm与ILDasm修改.Net exe(dll)文件
一.背景 最近项目组新上项目,交付的时间比较急迫,原本好的分支管理习惯没有遵守好,于是出现下面状况: 多个小伙伴在不同的分支上开发. 原本QA环境也存在一个阻碍性的bug A 一位同事在QA环境发布了 ...
- SQL常用语句,随时用随时更新
更多详细说明文档查询 http://www.postgres.cn/docs/9.5/infoschema-columns.html 1.1通过表名查询表的属性 SELECT * FROM sys.s ...
- nyoj 聪明的kk
聪明的kk 时间限制:1000 ms | 内存限制:65535 KB 难度:3 描述 聪明的"KK"非洲某国展馆的设计灵感源于富有传奇色彩的沙漠中陡然起伏的沙丘,体现出本国 ...
- 人工智能算法综述(二) RNN and LSTM
接上一篇 :AI算法综述 (一) RNN:循环神经网络 and LSTM 长短期记忆网络 LSTM就是一个RNN网络,外部的结构是一样的,主要是单元的内在结构不同.或者说LSTM是为了让RNN能够更好 ...
- Java程序员的情书
java程序员的情书 我能抽象出整个世界但是我不能抽象出你因为你在我心中是那么的具体所以我的世界并不完整我可以重载甚至覆盖这个世界里的任何一种方法但是我却不能重载对你的思念也许命中注定了 你在我的世界 ...
- awk、变量、运算符、if多分支
awk.变量.运算符.if多分支 awk: 语法 awk [options] 'commands' files option -F 定义字段分隔符,默认的分隔符是连续的空格或制表符 使用option中 ...
- Java面试题—初级(3)
21.ArrayList和Vector的区别 这两个类都实现了List接口(List接口继承了Collection接口),他们都是有序集合,即存储在这两个集合中的元素的位置都是有顺序的,相当于一种动态 ...
- 闭包(closure)
大牛的讲解,点击 我们首先需要有作用域的概念,点击 那么什么是闭包? 官方的解释是:闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分. 广义上的 ...