题目链接:https://loj.ac/problem/6184

每次询问给一些关键点,询问树上每个点离最近的关键点的距离(以后称为f(u))最大值是多少。

询问数比较大,但 \sum{K} 和n是一个级别的,我们考虑每次把询问的点建成虚树,在虚树上统计答案。那些不在虚树上的点的一定是通过虚树上的点走到的,它们的f(u)也都是通过虚树上的信息来维护的。

(update 7.17)建虚树的过程具体就是在栈中保存一条还未进行连边构图的树链,先按dfn序从小到大排序,如果新加入的点恰好是栈顶的后辈就直接把它加入栈中,如果不是就记他们的lca为u,我们只需要找到u从下到上第一个在栈中出现的祖先,并让它在top-1的位置,其他弹掉并建边即可。这是因为按dfn排过序,这时u->原栈顶 这条树链不可能再有新的分叉了,所以我们没必要保存它们,直接建边弹出栈就好了,而u以上的树链并不确定,我们还不能弹掉。至于弹后的栈顶,我们根据它是否是u来判断是否将u加入栈。做完上述操作后再将新节点加进去就没毛病了。

细节较多的分类讨论:

1、一个虚树上的节点的其他不在虚树中的子树中的答案,由于要排除掉在虚树上(或虚树边上)的儿子,需要预处理的时候对每个点开个有序的vector记录每个儿子子树最深值,查询时找到第一个不在虚树(边)上的儿子就break,这样统计这部分答案的均摊代价是O(虚树度数)的。

2、一条虚树上压缩后的长度大于1的边上的点的不在虚树中的子树中的答案。这部分我们需要通过预处理的倍增数组O(log(n))地来找到从上面的节点u走最优或从下面的节点v走最优的分界点k。然后d---k询问从d走到d->k中的一个点再往不包含d的子树中走的最大值,k->u询问u向下走不经过k的子树最大值。这两个东西都可以预先用倍增数组求出来,单次查询O(log2(n))。

注意:

1、最好强制让1是虚树的根,否则最后还得考虑虚树的根往上走再折回来的答案。

2、别忘了答案仍可能出在虚树上,要把虚树上节点的f值取个max。

接下来是一份AC代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(register int i=(a);i<=(b);++i)
typedef long long ll;
const int N = 200005;
int n,Q,nn;
struct la{
int gr,h[N],nxt[N],to[N],w[N];
inline void tu(int x,int y,int c=0){to[++gr]=y,nxt[gr]=h[x],h[x]=gr,w[gr]=c;}
}R,F; int K[N],cnt,tim,dfn[N];
int ff[N][24],m,dep[N],zhe[N][24],dwn[N][24];
int sta[N],top,f[N],g[N],bit[N];
vector<int> son[N];
bool cmp2(const int &a,const int &b){return f[a]>f[b];}
void dfs(int u,int fa){
dfn[u]=++tim;dep[u]=dep[fa]+1;
ff[u][0]=fa;
rep(i,1,m){
ff[u][i]=ff[ff[u][i-1]][i-1];
if(!ff[u][i])break;
}
for(int i=R.h[u];i;i=R.nxt[i]){
int d=R.to[i];
if(d==fa)continue;
dfs(d,u);
if(f[d]+1>f[u])g[u]=f[u],f[u]=f[d]+1;
else g[u]=max(g[u],f[d]+1);
son[u].push_back(d);
}
if(son[u].size()>0)sort(son[u].begin(),son[u].end(),cmp2);
for(int i=R.h[u];i;i=R.nxt[i]){
int d=R.to[i];
if(d==fa)continue;
zhe[d][0]=(son[u][0]==d?g[u]:f[u])+1;
dwn[d][0]=(son[u][0]==d?g[u]:f[u]);
} }
void beizeng(){
rep(i,1,m){
rep(u,1,n){
if(!ff[u][i])continue;
zhe[u][i]=max(zhe[ff[u][i-1]][i-1]+bit[i-1],zhe[u][i-1]);
dwn[u][i]=max(dwn[ff[u][i-1]][i-1],dwn[u][i-1]+bit[i-1]);
}
}
}
inline int lca(int x,int y){
if(dep[x]<dep[y])swap(x,y);
for(int i=m;i>=0;--i)if(dep[ff[x][i]]>=dep[y])x=ff[x][i];
if(x==y)return x;
for(int i=m;i>=0;--i)if(ff[x][i]!=ff[y][i])x=ff[x][i],y=ff[y][i];
return ff[x][0];
}
bool cmp(int x,int y){return dfn[x]<dfn[y];}
int dis[N],ans;
bool is[N],in[N];
void dp_up(int u){
if(is[u])dis[u]=0;
else dis[u]=0x3f3f3f3f;
for(int i=F.h[u];i;i=F.nxt[i]){
int d=F.to[i];
dp_up(d);
dis[u]=min(dis[u],dis[d]+F.w[i]);
}
}
int btw(int u,int d){
for(int i=0;i<=m;++i)if(d&bit[i])u=ff[u][i];
return u;
}
void get_up(int u,int d,int k){
if(!d)return;
int t=0;
for(int i=0;i<=m;++i)if(d&bit[i]){ ans=max(ans,t+k+zhe[u][i]);
u=ff[u][i],t+=bit[i];
} }
void get_dwn(int u,int d,int k){
if(d<=0)return;
for(int i=0;i<=m;++i)if(d&bit[i]){
d-=bit[i],ans=max(ans,k+d+dwn[u][i]);
u=ff[u][i];
if(!d)return;
}
}
int que[N],r;
void dp_dwn(int u){
int tmp=0,now;ans=max(ans,dis[u]);//printf("%d %d\n",u,dis[u]);
for(int i=F.h[u];i;i=F.nxt[i]){
int d=F.to[i];
dis[d]=min(dis[d],dis[u]+F.w[i]);
dp_dwn(d);
tmp=(dis[u]-dis[d]+F.w[i])/2;
if(F.w[i]>1){
if(tmp==F.w[i])get_up(d,tmp-1,dis[d]);
else if(tmp==0)get_dwn(d,F.w[i]-1,1+dis[u]);
else{
now=btw(d,tmp);
get_up(d,tmp,dis[d]);
get_dwn(now,F.w[i]-tmp-1,dis[u]+1);
}
now=btw(d,F.w[i]-1);
in[now]=1;
que[++r]=now;
}
}
}
void get_son(int u){
rep(i,0,(int)(son[u].size()-1)){
if(!in[son[u][i]]){ans=max(ans,dis[u]+f[son[u][i]]+1);break;}
}
for(int i=F.h[u];i;i=F.nxt[i]){
int d=F.to[i];
get_son(d);
}
F.h[u]=0;
in[u]=0;
}
int main(){
freopen("inception.in","r",stdin);
freopen("inception.out","w",stdout);
scanf("%d%d",&n,&Q);
m=log2(n);
int u,v;
rep(i,2,n)scanf("%d%d",&u,&v),R.tu(u,v),R.tu(v,u);
bit[0]=1;
rep(i,1,m)bit[i]=bit[i-1]<<1;
dfs(1,0);
beizeng();
while(Q--){
scanf("%d",&cnt);
rep(i,1,cnt)scanf("%d",&K[i]),is[K[i]]=1;
sort(K+1,K+cnt+1,cmp);
F.gr=0;ans=0;
sta[top=1]=1;//默认让1来做虚树的根,会省去一些麻烦
in[1]=1;
rep(i,1,cnt){
if(K[i]==1)continue;
int tmp=lca(sta[top],K[i]);
in[K[i]]=1;
if(tmp==sta[top]){sta[++top]=K[i];continue;}
while(top>1&&dfn[sta[top-1]]>=dfn[tmp]){
F.tu(sta[top-1],sta[top],dep[sta[top]]-dep[sta[top-1]]);
top--;
}
if(sta[top]!=tmp)F.tu(tmp,sta[top],dep[sta[top]]-dep[tmp]),sta[top]=tmp,in[tmp]=1;
sta[++top]=K[i];
}
while(top>1)F.tu(sta[top-1],sta[top],dep[sta[top]]-dep[sta[top-1]]),top--;
dp_up(1);
dp_dwn(1);
get_son(1);
printf("%d\n",ans);
//clear_is,gr
rep(i,1,cnt)is[K[i]]=0;
rep(i,1,r)in[que[i]]=0;
r=0;
}
return 0;
}

【loj6184】无心行挽(虚树+倍增)的更多相关文章

  1. JZOJ5143:无心行挽

    Description “What’s left to do when we’ve lost all hope?”“若内心万念俱灰,是否注定无心行挽?”------来自网易云音乐<Golden ...

  2. 【BZOJ】3572: [Hnoi2014]世界树 虚树+倍增

    [题意]给定n个点的树,m次询问,每次给定ki个特殊点,一个点会被最近的特殊点控制,询问每个特殊点控制多少点.n,m,Σki<=300000. [算法]虚树+倍增 [题解]★参考:thy_asd ...

  3. [BZO3572][HNOI2014]世界树:虚树+倍增

    分析 思维难度几乎为\(0\)的虚树码农(并不)题. 代码 #include <bits/stdc++.h> #define rin(i,a,b) for(register int i=( ...

  4. BZOJ 3572: [Hnoi2014]世界树 [虚树 DP 倍增]

    传送门 题意: 一棵树,多次询问,给出$m$个点,求有几个点到给定点最近 写了一晚上... 当然要建虚树了,但是怎么$DP$啊 大爷题解传送门 我们先求出到虚树上某个点最近的关键点 然后枚举所有的边$ ...

  5. P2495 [SDOI2011]消耗战 lca倍增+虚树+树形dp

    题目:给出n个点的树  q次询问  问切断 k个点(不和1号点联通)的最小代价是多少 思路:树形dp  sum[i]表示切断i的子树中需要切断的点的最小代价是多少 mi[i]表示1--i中的最小边权 ...

  6. bzoj3572[Hnoi2014] 世界树 虚树+dp+倍增

    [Hnoi2014]世界树 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1921  Solved: 1019[Submit][Status][Dis ...

  7. bzoj 2286(虚树+树形dp) 虚树模板

    树链求并又不会写,学了一发虚树,再也不虚啦~ 2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 5002  Sol ...

  8. BZOJ 2286 消耗战 (虚树+树形DP)

    给出一个n节点的无向树,每条边都有一个边权,给出m个询问,每个询问询问ki个点,问切掉一些边后使得这些顶点无法与顶点1连接.最少的边权和是多少.(n<=250000,sigma(ki)<= ...

  9. 【BZOJ-3572】世界树 虚树 + 树形DP

    3572: [Hnoi2014]世界树 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1084  Solved: 611[Submit][Status ...

随机推荐

  1. UVA10056 - What is the Probability ?(概率)

    UVA10056 - What is the Probability ? (概率) 题目链接 题目大意:有n个人玩游戏,一直到一个人胜出之后游戏就能够结束,要不然就一直从第1个到第n个循环进行,没人一 ...

  2. iOS开发——swift——swift与OC之间不得不知道的21点

    swift与OC之间不得不知道的21点   自6月的WWDC大会上由苹果的大神Chris Lattner向我们首次展示swift至今已经大半年时间了,虽然绝大部分软件公司代码里还都见不到一丁点swif ...

  3. 通过spark rdd 求取 特征的稀疏向量

    通过spark rdd 求取  特征的稀疏向量 spark 类标签的稀疏 特征向量 - bonelee - 博客园 http://www.cnblogs.com/bonelee/p/7814081.h ...

  4. struts2基础代码实现

    结构图: load.jsp <%@ page language="java" import="java.util.*" pageEncoding=&quo ...

  5. SQL SERVER 安装软件 及导入项目流程

    1.安装sqlsever2000及以上 数据库 (在百度上找安装文档) 创建账户 密码 2.解压SQL2000-KB884525-SP4-x86-CHS.EXE补丁 之后安装补丁 ,在安装补丁是会用到 ...

  6. poj2154 Color ——Polya定理

    题目:http://poj.org/problem?id=2154 今天学了个高端的东西,Polya定理... 此题就是模板,然而还是写了好久好久... 具体看这个博客吧:https://blog.c ...

  7. noip2002矩阵覆盖(搜索)

    矩阵覆盖 题目描述 在平面上有 n 个点(n <= 50),每个点用一对整数坐标表示.例如:当 n=4 时,4个点的坐标分另为:p1(1,1),p2(2,2),p3(3,6),P4(0,7),见 ...

  8. P4128 [SHOI2006]有色图

    传送门 数学渣渣看题解看得想死Ծ‸Ծ 首先发现这玩意儿看着很像polya定理 \[L=\frac{1}{|G|}\sum_{i\in G}m^{w(i)}\] 然而polya定理只能用来求点的置换,边 ...

  9. P3297 [SDOI2013]逃考

    传送门 完全看不出这思路是怎么来的-- 首先对于两个亲戚,他们监视范围的边界是他们连线的中垂线.那么对于一个亲戚来说它能监视的范围就是所有的中垂线形成的半平面交 然后如果某两个亲戚的监视范围有公共边, ...

  10. CSS实现居中的方式

    在介绍居中方式之前,简单介绍一下行内元素和块级元素. 行内元素 和其他元素都在同一行 高,行高及外边距和内边距部分可以改变 宽度只与内容有关 行内元素只能容纳文本或者其他行内元素 常用内联元素:a,i ...