题链:

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. 掌握SQLServer锁的相关概念

    一.为什么要引入锁 当多个用户同时对数据库的并发操作时会带来以下数据不一致的问题: ◆丢失更新 A,B两个用户读同一数据并进行修改,其中一个用户的修改结果破坏了另一个修改的结果,比如订票系统 ◆脏读 ...

  2. 作业07-Java GUI编程

    1. 本周学习总结 1.1 思维导图:Java图形界面总结 1.2 可选:使用常规方法总结其他上课内容. 关于事件.事件源.事件监听器的总结: 事件:用户在GUI上进行的操作,如鼠标单击.输入文字.关 ...

  3. 201621123057 《Java程序设计》第12周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多流与文件相关内容. 2. 面向系统综合设计-图书馆管理系统或购物车 使用流与文件改造你的图书馆管理系统或购物车. 2.1 简述如何 ...

  4. SQL数据库开发中的一些经典代码

    1.按姓氏笔画排序: Select * From TableName Order By CustomerName Collate Chinese_PRC_Stroke_ci_as  2.数据库加密: ...

  5. css的内容

    块级元素和行内元素的区别: 1. 行内元素部不能够设置宽度和高度.行内元素的宽度和高度是标签内容的宽度和高度.块级元素可以设置宽度和高度. 2. 块级元素会独占一行.而行内元素却部能够独占一行,只能和 ...

  6. JAVA_SE基础——28.封装

    黑马程序员blog... 面向对象三大特征:1. 封装2. 继承3  多态. 今天我们先学习第一大特征,封装. 封装:是指隐藏对象的属性和实现细节,仅对外提供公共访问方式. 好处:     1. 将变 ...

  7. python django的ManyToMany简述

    Django的多对多关系 在Django的关系中,有一对一,一对多,多对多的关系 我们这里谈的是多对多的关系 ==我们首先来设计一个用于示例的表结构== # -*- coding: utf-8 -*- ...

  8. EasyUI 动态创建对话框Dialog

    // 拒绝审批通过 function rejectApproval() { // 创建填写审批意见对话框 $("<div id='reject-comment'> </di ...

  9. Error creating document instance. Cause: org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 1; 前言中不允许有内容。

    Error creating document instance. Cause: org.xml.sax.SAXParseException;  lineNumber: 1; columnNumber ...

  10. Intent 的两种主要使用方法

    首先建立两个activity界面 Activity1如下 public class MainActivity extends AppCompatActivity { private Button bt ...