传送门

看到指定的总节点数小于等于 300000 就知道要搞虚树了

考虑如何在虚树确定每个议事处控制的节点数量

可以两遍dfs

第一遍求儿子对父亲的影响,第二遍求父亲对儿子影响

注意搜索顺序,这样就可以把影响扩展到其他子树了

如图:

初始时只有本身被影响

经过第一遍dfs后父亲被影响:

经过第二遍dfs后儿子被影响:

这样就可以考虑到所有情况了

然后对于虚树上的每一条边,考虑它的贡献

对于虚树上的一条边 $(u,v)$ ($u$ 是父节点,$u,v$被同一点控制),我们可以在原树上从$v$倍增跳到离$u$最近的节点$p_1$

那么设原树上的节点子树大小为 $sz$,那么虚树上那条边的贡献就是$sz[p_1]-sz[v]$

如果$u,v$不被同一点控制,那么中间肯定有一个分界点,我们也可以倍增从$v$在原树上跳到分界点$p_2$

设 $bel[x]$ 存节点 x 属于的节点

在$p_2$及以下的部分属于$bel[v]$,对$bel[v]的$贡献为 $sz[p_2]-sz[v]$

上面一直到 $u$ 的部分属于 $bel[u]$,贡献显然为 $sz[p_1]-sz[p_2]$

对于一个节点$x$,它还有一部分子树不在虚树上,设它们的数量为$sur[x]$,

初始时$sur[x]=sz[x]$,然后我们每次枚举一条虚树边就把$sur[x]$减去那条边的子树大小

($sur[x]=sur[x]-sz[p_1]$)

最后的$sur[x]$就是在$x$子树上但不在虚树上的节点数量了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
using namespace std;
typedef long long ll;
inline int read()
{
int x=,f=; char ch=getchar();
while(ch<''||ch>'') { if(ch=='-') f=-; ch=getchar(); }
while(ch>=''&&ch<='') { x=(x<<)+(x<<)+(ch^); ch=getchar(); }
return x*f;
}
const int N=3e5+;
int fir[N],from[N<<],to[N<<],cntt;//存原树
inline void add(int &a,int &b)
{
from[++cntt]=fir[a];
fir[a]=cntt; to[cntt]=b;
}
int n,m;
int dep[N],sz[N],f[N][],dfn[N],dfs_clock;//f是倍增数组
void dfs(int x)//预处理各种东西
{
dep[x]=dep[f[x][]]+; sz[x]=; dfn[x]=++dfs_clock;
for(int i=;i<=;i++) f[x][i]=f[f[x][i-]][i-];
for(int i=fir[x];i;i=from[i])
{
int v=to[i]; if(dfn[v]) continue;
f[v][]=x; dfs(v); sz[x]+=sz[v];
}
}
inline int LCA(int x,int y)//求LCA
{
if(dep[x]<dep[y]) swap(x,y);
for(int i=;i>=;i--) if(dep[f[x][i]]>=dep[y]) x=f[x][i];
if(x==y) return x;
for(int i=;i>=;i--)
if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
return f[x][];
}
int st[N<<],Top;
vector <int> v[N];//存虚树
inline void ins(int x)//插入一个节点到虚树里
{
if(Top==) { st[++Top]=x; return; }
int lca=LCA(x,st[Top]);
if(lca!=st[Top])
while(Top> && dfn[st[Top-]]>=dfn[lca]) v[st[Top-]].push_back(st[Top]),Top--;
if(lca!=st[Top]) v[lca].push_back(st[Top]),st[Top]=lca;
st[++Top]=x;
}
int cnt[N<<],bel[N<<],sur[N<<];
//cnt存每个议事处控制的数量
bool pd[N];//pd判断是否是议事处
void dfs1(int x)//第一遍dfs考虑儿子对父亲的贡献
{
int len=v[x].size(); sur[x]=sz[x];
if(pd[x]) bel[x]=x;//如果它本身是议事处,那么显然被自己控制
else bel[x]=;//注意多组询问,要清0
for(int i=;i<len;i++)
{
int t=v[x][i]; dfs1(t);//先向下dfs再考虑儿子的影响
if(!bel[x]) { bel[x]=bel[t]; continue; }//特判
int d1=dep[bel[t]]-dep[x],d2=dep[bel[x]]-dep[x];
if(d1<d2) bel[x]=bel[t];//考虑用儿子更新bel
else if(d1==d2&&bel[t]<bel[x]) bel[x]=bel[t];//注意如果距离相同取编号小的
}
}
inline int dis(int x,int y) { return dep[x]+dep[y]-*dep[LCA(x,y)]; }//求不在一条链上的两点距离
void dfs2(int x)//第二遍dfs考虑父亲对儿子的影响
{
int len=v[x].size();
for(int i=;i<len;i++)
{
int t=v[x][i],d1=dis(bel[x],t),d2=dis(bel[t],t);
if(d1<d2) bel[t]=bel[x];//考虑用父亲更新儿子
else if(d1==d2&&bel[x]<bel[t]) bel[t]=bel[x];//距离相同取编号小的
dfs2(t);//注意此时先更新儿子再dfs
}
}
void dp(int x)//最后dp统计贡献
{
int len=v[x].size(),p1,p2,t;
for(int i=;i<len;i++)
{
p1=p2=t=v[x][i]; dp(t);
for(int j=;j>=;j--) if(dep[f[p1][j]]>dep[x]) p1=f[p1][j];//倍增找到离x最近的节点
sur[x]-=sz[p1];//更新sur
if(bel[x]==bel[t]) cnt[bel[x]]+=sz[p1]-sz[t];//如果边上两点属于同一议事处直接更新贡献
else
{
for(int j=;j>=;j--)//否则倍增找到分界点
{
if(dep[f[p2][j]]<=dep[x]) continue;
int d1=dis(f[p2][j],bel[t]),d2=dis(f[p2][j],bel[x]);
if(d1<d2) p2=f[p2][j];
else if(d1==d2&&bel[t]<bel[x]) p2=f[p2][j];//同样如果距离一样取编号小的
}
cnt[bel[x]]+=sz[p1]-sz[p2];
cnt[bel[t]]+=sz[p2]-sz[t];//两边都要更新
}
}
cnt[bel[x]]+=sur[x];//最后贡献再加上sur[x]
v[x].clear();//记得清空
}
inline bool cmp(const int &a,const int &b) { return dfn[a]<dfn[b]; }//按dfs序排序
int d[N],dd[N];
inline void solve()//处理询问
{
int t=read();
for(int i=;i<=t;i++)
{
d[i]=dd[i]=read();
pd[d[i]]=;
}
sort(d+,d+t+,cmp); st[Top=]=;
for(int i=(pd[] ? : );i<=t;i++) ins(d[i]);//插入
while(Top>) v[st[Top-]].push_back(st[Top]),Top--;
dfs1(); dfs2(); dp();
for(int i=;i<=t;i++)
{
printf("%d ",cnt[dd[i]]);
pd[dd[i]]=cnt[dd[i]]=;//记得清空
}
printf("\n");
}
int main()
{
// freopen("data.in","r",stdin);
// freopen("data.out","w",stdout);
int a,b; n=read();
for(int i=;i<n;i++)
{
a=read(),b=read();
add(a,b); add(b,a);
}
dfs();
m=read();
while(m--) solve();
return ;
}

P3233 [HNOI2014]世界树的更多相关文章

  1. ●洛谷P3233 [HNOI2014]世界树

    题链: https://www.luogu.org/problemnew/show/P3233题解: 虚树,dp,倍增. 首先对于每个询问,要把虚树建出来,这一步就从略了.这里着重分享一下如何求答案. ...

  2. 洛谷P3233 [HNOI2014]世界树

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

  3. luogu P3233 [HNOI2014]世界树

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

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

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

  5. 题解 P3233 [HNOI2014]世界树

    题目传送门 解题思路 正解当然是虚树了. 首先对于原树以及虚树各开一个结构体存边,这个不用多说. 然后我们先 DFS 一遍,求出各个节点的时间戳,子树大小,深度以及父亲节点,并初始化倍增 LCA . ...

  6. [BZOJ3572][Hnoi2014]世界树

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

  7. BZOJ 3572: [Hnoi2014]世界树

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

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

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

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

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

随机推荐

  1. javascript使用技巧总结,不断更新...

    1.使用a标签来获得当前页面相对地址的绝对地址 function getAbsoluteUrl(url){ var a; if(!a) a = document.createElement('a'); ...

  2. opencv3 图片模糊操作-均值滤波 高斯滤波 中值滤波 双边滤波

    #include <iostream>#include <opencv2/opencv.hpp> using namespace std;using namespace cv; ...

  3. Docker学习笔记_安装和使用Apache

    一.准备 1.宿主机OS:Win10 64位 2.虚拟机OS:Ubuntu18.04 3.账号:docker 二.安装 1.搜索镜像                                  ...

  4. Nginx 模块开发

    Nginx 模块概述 Nginx 模块有三种角色: 处理请求并产生输出的 Handler 模块 : 处理由  Handler  产生的输出的 Filter (滤波器)模块: 当出现多个后台 服务器时, ...

  5. 2014年:Linux和开源的福祸之年

    (1)Heartbleed漏洞 Heartbleed漏洞,是今年开源软件曝出的最大糗事.Heartbleed漏洞是OpenSSL的重大漏洞,这项严重缺陷(CVE-2014-0160)的产生是由于未能在 ...

  6. SP1557 GSS2 - Can you answer these queries II

    一开始看不懂题解,看懂了题解之后觉得还是挺妙的. 好多题解里都提到了HH的项链,但是我觉得关系并不大啊…… 先把所有询问离线下来按照右端点排序,按照询问的要求一个一个加入数字,怎么加入数字,我们设计一 ...

  7. pip镜像源配置

    #配置文件路径:%HOME%\pip\pip.ini,不存在新建即可 #在 Windows 2000 以上版本里,%HOME% 目录指的是系统盘下的“\Documents and Settings\你 ...

  8. Java日志组件logback使用:加载非类路径下的配置文件并设置定时更新

    Java日志组件logback使用:加载非类路径下的配置文件并设置定时更新 摘自: https://blog.csdn.net/johnson_moon/article/details/7887449 ...

  9. 运行maven build报错No goals have been specified for this build.

    运行maven报错: [ERROR] No goals have been specified for this build. You must specify a valid lifecycle p ...

  10. 设计模式05: Prototype 原型模式(创建型模式)

    Prototype 原型模式(创建型模式) 依赖关系的倒置抽象不应该依赖于实现细节,细节应该依赖于抽象.对所有的设计模式都是这样的. -抽象A直接依赖于实现细节b -抽象A依赖于抽象B,实现细节b依赖 ...