博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【BZOJ】3572: [Hnoi2014]世界树 虚树+倍增
阅读量:6089 次
发布时间:2019-06-20

本文共 4340 字,大约阅读时间需要 14 分钟。

【题意】给定n个点的树,m次询问,每次给定ki个特殊点,一个点会被最近的特殊点控制,询问每个特殊点控制多少点。n,m,Σki<=300000。

【算法】虚树+倍增

【题解】★参考:

对询问建立虚树,然后DFS统计虚树上每个点被哪个点控制,记为belong[x]。

统计的方法是从下往上DFS得到x来自x子树的控制点,再从上往下DFS得到x来自非x子树的控制点。

令D(x,y)表示点x和点y之间的路径长度,比较x的不同控制点y的方式是取D(x,y)的最小值(相同时比较编号)。

 

依此考虑虚树上的每一条边(x,y),记z为同时是x的儿子和y的祖先的点,令f[x]表示点x的答案。

如果belong[x]=belong[y],那么f[belong[x]]+=size[z]-size[y]

否则,从y倍增找到分界点mid,满足mid及以下由y控制,mid以上由x控制。

倍增过程中,记A=D(belong[x],mid),B=D(belong[x],mid),当满足A>B或A=B&&belong[x]>belong[y]时进行倍增。

找到分界点mid后,f[belong[x]]+=size[x]-size[m],f[belong[y]+=size[m]-size[y]。

 

最后要处理没有出现在虚树路径上的点(包括虚树节点),这些点没有在上述过程中被统计。

记rem[x]表示子树x中未被统计的节点数,初始rem[x]=size[x],特别的rem[a[1]]=n(a[1]是虚树最高节点,n是原树总点数,这样是为了保存a[1]往上延伸的节点)

每次处理完边(x,y),rem[x]-=size[z]。

全部做完后,对虚树的每个点f[belong[x]]+=rem[x]。

#include
#include
#include
#include
using namespace std;int read(){ int s=0,t=1;char c; while(!isdigit(c=getchar()))if(c=='-')t=-1; do{s=s*10+c-'0';}while(isdigit(c=getchar())); return s*t;}const int maxn=300010;int n,N,size[maxn],in[maxn],ou[maxn],f[maxn],a[maxn*2],b[maxn];int deep[maxn],tot,first[maxn],belong[maxn],st[maxn];int rem[maxn];bool v[maxn];namespace cyc{ int first[maxn],tot,dfsnum=0,f[maxn][30]; struct edge{
int v,from;}e[maxn*2]; void insert(int u,int v){tot++;e[tot].v=v;e[tot].from=first[u];first[u]=tot;} void dfs(int x,int fa){ in[x]=++dfsnum;size[x]=1; for(int j=1;(1<
<=deep[x];j++)f[x][j]=f[f[x][j-1]][j-1]; for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa){ deep[e[i].v]=deep[x]+1; f[e[i].v][0]=x; dfs(e[i].v,x); size[x]+=size[e[i].v]; } ou[x]=dfsnum; } int lca(int x,int y){ if(deep[x]
=0;j--)if((1<
<=deep[x]&&f[x][j]!=f[y][j]){ x=f[x][j];y=f[y][j]; } return f[x][0]; } void build(){ n=read(); for(int i=1;i
=0;i--)if((1<
<=deep[z]&&deep[cyc::f[z][i]]>=deep[x]+1)z=cyc::f[z][i]; rem[x]-=size[z]; if(belong[x]==belong[y]){f[belong[x]]+=size[z]-size[y];return;}// int m=y; for(int i=20;i>=0;i--)if((1<
<=deep[m]){ if(deep[cyc::f[m][i]]
belong[y]))m=cyc::f[m][i];// } f[belong[x]]+=size[z]-size[m]; f[belong[y]]+=size[m]-size[y];}void work(){ int last=read();N=last; for(int i=1;i<=N;i++)a[i]=read(),f[b[i]=a[i]]=0,v[a[i]]=1; sort(a+1,a+N+1,cmp); for(int i=1;i
View Code

 (倍增求LCA)

 

 

 

注意,由于多次调用LCA,所以用倍增LCA算法有很大的常数,甚至在LOJ会导致TLE。

但是倍增求LCA会使过程更简洁。

下面这份代码是树链剖分求LCA,代码可读性不高。

#include
#include
#include
#include
using namespace std;int read(){ int s=0,t=1;char c; while(!isdigit(c=getchar()))if(c=='-')t=-1; do{s=s*10+c-'0';}while(isdigit(c=getchar())); return s*t;}const int maxn=300010;int n,N,size[maxn],in[maxn],ou[maxn],f[maxn],a[maxn*2],b[maxn];int deep[maxn],tot,first[maxn],belong[maxn],st[maxn];int rem[maxn];bool v[maxn];namespace cyc{ int first[maxn],tot,dfsnum=0,f[maxn][30]; struct edge{
int v,from;}e[maxn*2]; void insert(int u,int v){tot++;e[tot].v=v;e[tot].from=first[u];first[u]=tot;} void dfs(int x,int fa){ in[x]=++dfsnum;size[x]=1; for(int j=1;(1<
<=deep[x];j++)f[x][j]=f[f[x][j-1]][j-1]; for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa){ deep[e[i].v]=deep[x]+1; f[e[i].v][0]=x; dfs(e[i].v,x); size[x]+=size[e[i].v]; } ou[x]=dfsnum; } int lca(int x,int y){ if(deep[x]
=0;j--)if((1<
<=deep[x]&&f[x][j]!=f[y][j]){ x=f[x][j];y=f[y][j]; } return f[x][0]; } void build(){ n=read(); for(int i=1;i
=0;i--)if((1<
<=deep[z]&&deep[cyc::f[z][i]]>=deep[x]+1)z=cyc::f[z][i]; rem[x]-=size[z]; if(belong[x]==belong[y]){f[belong[x]]+=size[z]-size[y];return;}// int m=y; for(int i=20;i>=0;i--)if((1<
<=deep[m]){ if(deep[cyc::f[m][i]]
belong[y]))m=cyc::f[m][i];// } f[belong[x]]+=size[z]-size[m]; f[belong[y]]+=size[m]-size[y];}void work(){ int last=read();N=last; for(int i=1;i<=N;i++)a[i]=read(),f[b[i]=a[i]]=0,v[a[i]]=1; sort(a+1,a+N+1,cmp); for(int i=1;i
View Code

 

upd:复习一下。

【建虚树】

第一步、所有关键点按DFS序排序,加入LCA后排序去重。

第二步、按顺序用栈维护构造出虚树。

第三步、虚树DP后清零。

【此题的虚树DP】

第一步、处理虚树上点与点。

第二步、处理虚树边上点的子树。

第三步、处理虚树点的 [ 不在虚树上的孩子 ] 子树。(通过减虚树孩子的sz得到)

转载于:https://www.cnblogs.com/onioncyc/p/8480787.html

你可能感兴趣的文章
【转】go语言的字节序
查看>>
Mysql系列一:SQL入门
查看>>
蔡文胜谈互联网变化
查看>>
SGU 208 Toral Tickets(polay计数)
查看>>
XML属性
查看>>
【MFC】序列化(Serialize)、反序列化(Deserialize)
查看>>
【转】编译Android系统源码和内核源码
查看>>
【157】扫描仪使用
查看>>
A Complete Tutorial on Tree Based Modeling from Scratch (in R & Python)
查看>>
在Windows Server2008R2中导入Excel不能使用Jet 4.0的解决方法
查看>>
C# 如何利用反射来加载程序集,并调用程序集中有关类的方法
查看>>
OO开发思想:面向对象的开发方法(Object oriented,OO)
查看>>
SQLServer2012第一印象【多图】
查看>>
android 界面布局 很好的一篇总结 【转】
查看>>
你必须知道的 34 个简单实用的 Ubuntu 快捷键
查看>>
MATLAB的基本元素
查看>>
ie6绝对定位的块会被select元素遮挡的解决方案
查看>>
DOM4J解析XML
查看>>
Oracle 11g 的server结果缓存result_cache_mode
查看>>
浅谈sql的字符分割
查看>>