题链:

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]世界树的更多相关文章

  1. 洛谷P3233 [HNOI2014]世界树

    虚树= = #include<cstdio> #include<cstdlib> #include<algorithm> #include<cstring&g ...

  2. 洛谷 P3233 [HNOI2014]世界树(虚树+dp)

    题面 luogu 题解 数据范围已经告诉我们是虚树了,考虑如何在虚树上面\(dp\) 以下摘自hzwer博客: 构建虚树以后两遍dp处理出虚树上每个点最近的议事处 然后枚举虚树上每一条边,考虑其对两端 ...

  3. 洛谷P3233 世界树 [HNOI2014] 虚树

    正解:虚树 解题报告: 传送门! 首先看到这种就要想到虚树这个是毫无疑问的QwQ 建虚树什么的都可以循规蹈矩地做,不说辣,具体可以看下虚树学习笔记什么的看下板子 但是建好虚树之后怎么搞还是有点儿讲究, ...

  4. 洛谷P3233 世界树

    题意:给定树上k个关键点,每个点属于离他最近,然后编号最小的关键点.求每个关键点管辖多少点. 解:虚树 + DP. 虚树不解释.主要是DP.用二元组存虚树上每个点的归属和距离.这一部分是二次扫描与换根 ...

  5. 洛谷 P3237 [HNOI2014]米特运输 解题报告

    P3237 [HNOI2014]米特运输 题目描述 米特是\(D\)星球上一种非常神秘的物质,蕴含着巨大的能量.在以米特为主要能源的D星上,这种米特能源的运输和储存一直是一个大问题. \(D\)星上有 ...

  6. 洛谷 P3235 [HNOI2014]江南乐 解题报告

    P3235 [HNOI2014]江南乐 Description 两人进行 T 轮游戏,给定参数 F ,每轮给出 N 堆石子,先手和后手轮流选择石子数大于等于 F 的一堆,将其分成任意(大于1)堆,使得 ...

  7. 洛谷P3237 [HNOI2014]米特运输(树形dp)

    解题报告 题干 米特是D星球上一种非常神秘的物质,蕴含着巨大的能量.在以米特为主要能源的D星上,这种米特能源的运输和储存一直是一个大问题. D星上有N个城市,我们将其顺序编号为1到N,1号城市为首都. ...

  8. 洛谷P3235 [HNOI2014]江南乐(Multi-SG)

    题目描述 小A是一个名副其实的狂热的回合制游戏玩家.在获得了许多回合制游戏的世界级奖项之后,小A有一天突然想起了他小时候在江南玩过的一个回合制游戏. 游戏的规则是这样的,首先给定一个数F,然后游戏系统 ...

  9. luogu P3233 [HNOI2014]世界树

    传送门 我是什么时候写的这题的qwq 首先,发现关键点的总数被限制了,很自然想到虚树,并且,对于一个关键点,他管理的点显然是一个联通块 然后把虚树先建出来,然后两次dfs,第一次是向祖先更新离每个点最 ...

随机推荐

  1. beta冲刺6

    前言:此篇是补昨天凌晨的.后面有更新但是太晚了就没有即使更新.所以现在过来更新一下. 昨天的未完成: 用户测试+测试报告 目前剩下的功能点:输入内容检测 我的社团输出显示格式调整. 今天的完成: 我的 ...

  2. alpha-咸鱼冲刺day6

    一,合照 emmmmm.自然还是没有的. 二,项目燃尽图 三,项目进展 !!!QAQ可以做到跟数据库交互了!!!!先来撒花花!(然后继续甲板) (然后就没有进展了.翻车+1s) 四,问题困难 数据库交 ...

  3. 敏捷冲刺每日报告——Day2

    1.情况简述 Alpha阶段第一次Scrum Meeting 敏捷开发起止时间 2017.10.26 00:00 -- 2017.10.27 00:00 讨论时间地点 2017.10.26晚9:30, ...

  4. 20162327WJH第五周作业

    学号 20162327 <程序设计与数据结构>第5周学习总结 教材学习内容总结 1.java是一种面向对象的语言.面向对象是一种编程方法.更是一种思维方式. 2.面向对象编程的终极目标是消 ...

  5. 第十一条:谨慎的覆盖clone()方法

    一个类要想实现克隆,需要实现Cloneable接口,表明这个类的对象具有克隆的功能. Cloneable接口是一个mixin接口,它里面并没有任何的抽象方法,类似的接口有Serializable接口, ...

  6. Tornado 网站demo 三

    模板 修改index.py #!/usr/bin/env Python # coding=utf-8 import tornado.web import methods.readdb as mrd c ...

  7. loadrunner下载资源时步骤下载超时 (120 seconds) 已过期

    下载资源所用时间超过120秒时,就会报出这个错误,解决方法是设置加大超时时间 运行时设置(快捷键F4) Internet 协议--首选项--高级--选项--General--步骤下载超时(秒) 可以把 ...

  8. WebAPI 跨域解决方案.

    先下载支持跨域的.dll,然后using System.Web.Http.Cors. 我把webapi解决方案部署到IIS上了.测试过后可以解决跨域. 方案一(用了*号,这样有安全隐患.): 直接在w ...

  9. Python内置函数(21)——tuple

    英文文档: The constructor builds a tuple whose items are the same and in the same order as iterable's it ...

  10. WPF 自定义RadioButton样式

    一.RadioButton基本样式 RadioButton基本样式包含两种状态,这里也是使用两张图片来代替两种状态,当然你也可以通过IconFont或Path来替换这两种状态. 效果如下: 样式代码如 ...