题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3572

关于虚树:https://www.cnblogs.com/zzqsblog/p/5560645.html

构造方法:

  先把关键点按 dfs 序排序,然后依次插入树中;

  插入当前点 cr 的时候,求 lca = get_lca( cr , sta[top] ) ;如果 dep[ sta[top] ] >= dep[lca] ,就一直弹栈;

  弹栈结束后,看看现在的 sta[ top ] 是不是就是 lca 了,如果不是,就 sta[ ++ top ] = lca ;同时 fa[ sta[top+1] ] = lca , fa[ lca ] = sta[ top ] ;

  把 cr 也加入栈中,即 sta[++top] = cr , fa[ cr ] = lca 。

sta[ 1 ] 就是虚树的根。

关于这道题:http://hzwer.com/6804.html

建好虚树,先换根 dp 得出虚树上的每个点应该被哪个点控制。换根的时候不用去掉该子树的贡献,因为不会有影响。

然后枚举虚树上的每条边 ( cr , fa ),用倍增在边上找到最浅的应该 “被控制 cr 的点控制” 的点 v ,然后 siz[v] - siz[cr] 和 siz[tv] - siz[v] 分别贡献即可,其中 tv 是 fa 在 v 方向的直接孩子。

关于不是虚树的点也不在虚树边上的那些点,自己的方法是在换根 dp 的时候处理;那个时候枚举孩子 v 的时候通过找 tv ,可以知道每个虚树上的点 cr 的不在虚树上的孩子们的 siz 和,直接贡献给控制 cr 的那个点即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define mkp make_pair
#define fir first
#define sec second
using namespace std;
int rdn()
{
int ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return fx?ret:-ret;
}
const int N=3e5+,K=;
int n,hd[N],xnt,to[N<<],nxt[N<<];
int dfn[N],dep[N],pre[N][K+],bin[K+],lg[N],siz[N],sm[N];
pair<int,int> dp[N];
int m,ans[N],tt,sta[N],tot,fa[N],h2[N],xt2,t2[N<<],nt2[N<<];
struct Node{int v,id;}q[N]; bool cmp(Node x,Node y){return dfn[x.v]<dfn[y.v];}
void add(int x,int y){to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;}
void ad2(int x,int y){t2[++xt2]=y;nt2[xt2]=h2[x];h2[x]=xt2;}
void ini_dfs(int cr,int fa)
{
dfn[cr]=++tot; dep[cr]=dep[fa]+;
pre[cr][]=fa; siz[cr]=;
for(int t=,u;(u=pre[pre[cr][t-]][t-]);t++)
pre[cr][t]=u;
for(int i=hd[cr],v;i;i=nxt[i])
if((v=to[i])!=fa)
{
ini_dfs(v,cr); siz[cr]+=siz[v];
}
}
int get_lca(int x,int y)
{
if(dep[x]<dep[y])swap(x,y);
for(int t=lg[dep[x]-dep[y]];t>=;t--)
if(dep[pre[x][t]]>=dep[y])
x=pre[x][t];
if(x==y)return x;
for(int t=lg[dep[x]];t>=;t--)
if(pre[x][t]!=pre[y][t])
x=pre[x][t],y=pre[y][t];
return pre[x][];
}
void build()
{
sort(q+,q+m+,cmp); tt=m; tot=;
for(int i=;i<=m;i++)
{
int cr=q[i].v;
if(!tot){sta[++tot]=cr;dp[cr]=mkp(,cr);continue;}
int lca=get_lca(cr,sta[tot]);
while(dep[sta[tot]]>dep[lca])tot--;
if(sta[tot]!=lca)
{
q[++tt].v=lca; fa[sta[tot+]]=lca;
fa[lca]=sta[tot]; sta[++tot]=lca;
dp[lca]=mkp(N,);
}
fa[cr]=lca; sta[++tot]=cr; dp[cr]=mkp(,cr);
}
for(int i=;i<=tt;i++)if(fa[q[i].v])ad2(fa[q[i].v],q[i].v);
}
void dfs(int cr,int fa)
{
for(int i=h2[cr],v;i;i=nt2[i])
if((v=t2[i])!=fa)
{
dfs(v,cr);
int tp1=dp[v].fir+dep[v]-dep[cr],tp2=dp[cr].fir;
if(tp1<tp2||(tp1==tp2&&dp[v].sec<dp[cr].sec))
dp[cr].fir=tp1,dp[cr].sec=dp[v].sec;
}
}
int fnd2(int cr,int fa)
{
int d=dep[cr]-dep[fa]-;
while(d)
{
int lbt=(d&-d);
cr=pre[cr][lg[lbt]]; d-=lbt;
}
return cr;
}
void dfsx(int cr,int fa)
{
int tp1=dp[fa].fir+dep[cr]-dep[fa],tp2=dp[cr].fir;
if(fa&&(tp1<tp2||(tp1==tp2&&dp[fa].sec<dp[cr].sec)))
dp[cr].fir=tp1,dp[cr].sec=dp[fa].sec;
int s=siz[cr];
for(int i=h2[cr],v;i;i=nt2[i])
if((v=t2[i])!=fa)
{
int tv=fnd2(v,cr);
s-=siz[tv]; dfsx(v,cr);
}
sm[dp[cr].sec]+=s-;//-1 for zj
}
int fnd(int cr,int fa)
{
bool fg=(dp[cr].sec<dp[fa].sec);
int x=dp[cr].fir,y=dp[fa].fir,d1=dep[cr],d2=dep[fa];
for(int t=lg[dep[cr]-dep[fa]];t>=;t--)
{
int d=dep[pre[cr][t]];
int u=d1-d+x,v=d-d2+y;
if(u<v||(u==v&&fg))cr=pre[cr][t];
}
return cr;
}
bool In(int cr,int fa){return dfn[cr]>=dfn[fa]&&dfn[cr]<dfn[fa]+siz[fa];}
void solve()
{
for(int i=;i<=m;i++)sm[q[i].v]=;
int rt=sta[]; dfs(rt,); dfsx(rt,);
for(int i=;i<=tt;i++)sm[dp[q[i].v].sec]++;
for(int i=;i<=tt;i++)
{
int cr=q[i].v,f=fa[cr];
if(!f) {sm[dp[cr].sec]+=n-siz[cr];continue;}
int v=fnd(cr,f),tv=fnd2(cr,f);
sm[dp[cr].sec]+=siz[v==f?tv:v]-siz[cr];//
sm[dp[f].sec]+=siz[tv]-siz[v==f?tv:v];//tv//
}
for(int i=;i<=m;i++)ans[q[i].id]=sm[q[i].v];
for(int i=;i<=m;i++)printf("%d ",ans[i]); puts("");
}
int main()
{
n=rdn();
for(int i=,u,v;i<n;i++)
u=rdn(),v=rdn(),add(u,v),add(v,u);
bin[]=;for(int i=;i<=K;i++)bin[i]=bin[i-]<<;
for(int i=;i<=n;i++)lg[i]=lg[i>>]+;
ini_dfs(,); int Q=rdn();
while(Q--)
{
for(int i=;i<=tt;i++)h2[q[i].v]=; xt2=;
for(int i=;i<=tt;i++)fa[q[i].v]=;///
m=rdn(); for(int i=;i<=m;i++)q[i].v=rdn(),q[i].id=i;
build(); solve();
}
return ;
}

bzoj 3572 [Hnoi2014]世界树——虚树的更多相关文章

  1. bzoj 3572: [Hnoi2014]世界树 虚树 && AC500

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

  2. BZOJ 3572: [Hnoi2014]世界树 虚树 树形dp

    https://www.lydsy.com/JudgeOnline/problem.php?id=3572 http://hzwer.com/6804.html 写的时候参考了hzwer的代码,不会写 ...

  3. bzoj 3572: [Hnoi2014]世界树 虚树

    题目: Description 世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界.在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生 ...

  4. BZOJ 3572 [HNOI2014]世界树 (虚树+DP)

    题面:BZOJ传送门 洛谷传送门 题目大意:略 细节贼多的虚树$DP$ 先考虑只有一次询问的情况 一个节点$x$可能被它子树内的一个到x距离最小的特殊点管辖,还可能被管辖fa[x]的特殊点管辖 跑两次 ...

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

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

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

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

  7. BZOJ 3572: [Hnoi2014]世界树

    BZOJ 3572: [Hnoi2014]世界树 标签(空格分隔): OI-BZOJ OI-虚数 OI-树形dp OI-倍增 Time Limit: 20 Sec Memory Limit: 512 ...

  8. bzoj 3572 [Hnoi2014]世界树(虚树+DP)

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

  9. 【BZOJ3572】[Hnoi2014]世界树 虚树

    [BZOJ3572][Hnoi2014]世界树 Description 世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界.在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森 ...

随机推荐

  1. WebSocket教程(一)

    一.websocket与http WebSocket是HTML5出的东西(协议),也就是说HTTP协议没有变化,或者说没关系,但HTTP是不支持持久连接的(长连接,循环连接的不算) 首先HTTP有 1 ...

  2. 使用HttpClient 发送 GET、POST、PUT、Delete请求及文件上传

    package org.caeit.cloud.dev.util; import java.io.File; import java.io.IOException; import java.io.Un ...

  3. [LeetCode] 29. Divide Two Integers(不使用乘除取模,求两数相除) ☆☆☆

    转载:https://blog.csdn.net/Lynn_Baby/article/details/80624180 Given two integers dividend and divisor, ...

  4. adobe flash player不是最新版本

    adobe flash player不是最新版本

  5. Excel中输入身份证后3位变成0,怎么办?

    1.问题介绍: 1)问题: 在使用excel输入身份证号时,会发现直接输入数字后最后面的三位数字都变成0了,这可怎么办呢?

  6. POJ 3685 Matrix 二分 函数单调性 难度:2

      Memory Limit: 65536K Total Submissions: 4637   Accepted: 1180 Description Given a N × N matrix A, ...

  7. hdu3613

    题解: EX_KMP 网上似乎说kmp也可以,但是我交了一发代码没过 然后标记一下哪一些前缀和后缀会问 暴力枚举拆开了的位置 代码: #include<cstdio> #include&l ...

  8. css 课堂笔记

    css:层叠样式表  Cascading( [kæ'skeɪdɪŋ] 级联)Style Sheet css基本语法结构:选择器{ 属性:值; 属性:值: ... } 三种主要的选择器: 标签选择器: ...

  9. OC基础:数组.字典.集 分类: ios学习 OC 2015-06-18 18:58 47人阅读 评论(0) 收藏

    ==============NSArray(不可变数组)=========== NSArray,继承自NSObject  用来管理(储存)一些有序的对象,不可变数组. 创建一个空数组 NSArray ...

  10. Ubuntu终端点击确定按钮的方法

    Ubuntu终端里出现需要点击 确定 按钮的时候,直接鼠标点击 确定 是不生效的,这个时候需要利用tab键选中这个 确定 按钮,然后回车键就可以了.