bzoj 4539: [Hnoi2016]树
Description
小A想做一棵很大的树,但是他手上的材料有限,只好用点小技巧了。开始,小A只有一棵结点数为N的树,结
点的编号为1,2,…,N,其中结点1为根;我们称这颗树为模板树。小A决定通过这棵模板树来构建一颗大树。构建过
程如下:(1)将模板树复制为初始的大树。(2)以下(2.1)(2.2)(2.3)步循环执行M次(2.1)选择两个数字a,b,
其中1<=a<=N,1<=b<=当前大树的结点数。(2.2)将模板树中以结点a为根的子树复制一遍,挂到大树中结点b的下
方(也就是说,模板树中的结点a为根的子树复制到大树中后,将成为大树中结点b的子树)。(2.3)将新加入大树
的结点按照在模板树中编号的顺序重新编号。例如,假设在进行2.2步之前大树有L个结点,模板树中以a为根的子
树共有C个结点,那么新加入模板树的C个结点在大树中的编号将是L+1,L+2,…,L+C;大树中这C个结点编号的大小
顺序和模板树中对应的C个结点的大小顺序是一致的。下面给出一个实例。假设模板树如下图:
根据第(1)步,初始的大树与模板树是相同的。在(2.1)步,假设选择了a=4,b=3。运行(2.2)和(2.3)后,得到新的
大树如下图所示
现在他想问你,树中一些结点对的距离是多少。
Input
第一行三个整数:N,M,Q,以空格隔开,N表示模板树结点数,M表示第(2)中的循环操作的次数,Q 表示询问数
量。接下来N-1行,每行两个整数 fr,to,表示模板树中的一条树边。再接下来M行,每行两个整数x,to,表示将模
板树中 x 为根的子树复制到大树中成为结点to的子树的一次操作。再接下来Q行,每行两个整数fr,to,表示询问
大树中结点 fr和 to之间的距离是多少。N,M,Q<=100000
Output
输出Q行,每行一个整数,第 i行是第 i个询问的答案。
Sample Input
1 4
1 3
4 2
4 5
4 3
3 2
6 9
1 8
5 3
Sample Output
3
3
HINT
经过两次操作后,大树变成了下图所示的形状:
结点6到9之间经过了6条边,所以距离为6;类似地,结点1到8之间经过了3条边;结点5到3之间也经过了3条边。
Source
树链剖分+倍增+主席树+二分;
真的是一道码得爽彻心扉的大码农题,在考场上写这个题真的就是在赌博...;
首先因为直接复制的话点数是n^2的,不可能存得下,我们很容易想到构一棵超级树,树上的每个节点都代表一个复制上去的子树;
对于超级树上的节点存储两个信息:子树的根,节点的序号范围;
我们考虑如何查询题目中所说的大树的点在超级树中的编号,这个通过二分可以找到在超级树的哪个节点上;
然后我们想知道他对应原树的哪个节点,因为子树内的编号顺序与原树相同,那么我们相当于查询该超级点所代表的子树的第k小编号即可,那么我们对原树按dfs序建主席树就能查询了;
然后我们考虑如何处理询问:
首先我们需要找到这两个点对应在超级树中的位置,如果在同一个超级点中,那么直接查询两点子在原树上的距离;
如果不是,每个点先跳到该超级点所代表子树的根,然后往上跳到超级树上lca的儿子(用倍增来搞),然后进入lca所代表的子树中,然后查询两点间的距离;(如果lca为其中某一个节点则特殊处理)
在询问过程中我们需要知道一些值:
1.在一棵子树内部移动的距离,这个对原树进行树链剖分就可以很好地得到;
2.超级树上的边权,设新的点x,连在超级树的点y所代表的子树的z节点的下面,那么x->y的边权应该代表从x的子树的根到y的子树的根的距离,所以为dis(z,y.rt)+1;
然后我们用距离的前缀和数组相减即得到了在超级树上移动的距离;
这样就可以嘴巴AC了,但细节是真的多,我不把原树当超级点搞就错了;
//MADE BY QT666
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=800050;
int n,m,Q,tot;
ll sum;
int head[N],to[N],nxt[N],cnt,Father[N];
ll deep[N],d[N],dep[N];
void lnk(int x,int y){
to[++cnt]=y,nxt[cnt]=head[x],head[x]=cnt;
to[++cnt]=x,nxt[cnt]=head[y],head[y]=cnt;
}
struct data{
int dfn[N],ed[N],id[N],tmp,son[N],top[N],fa[N],sz[N];
int rt[N],ls[N*4],rs[N*4],size[N*4],tt;
void dfs1(int x,int f){
sz[x]=1;d[x]=deep[x];Father[x]=fa[x];
for(int i=head[x];i;i=nxt[i]){
int y=to[i];if(y==f) continue;
fa[y]=x;deep[y]=deep[x]+1;dfs1(y,x);
sz[x]+=sz[y];
if(sz[y]>sz[son[x]]) son[x]=y;
}
}
void dfs2(int x,int f){
top[x]=f;dfn[x]=++tmp;id[tmp]=x;
if(son[x]) dfs2(son[x],f);
for(int i=head[x];i;i=nxt[i]){
int y=to[i];if(y==fa[x]||y==son[x]) continue;
dfs2(y,y);
}
ed[x]=tmp;
}
int Lca(int x,int y){
while(top[x]!=top[y]){
if(deep[top[x]]<deep[top[y]]) swap(x,y);
x=fa[top[x]];
}
if(deep[x]<deep[y]) swap(x,y);
return y;
}
int dis(int x,int y){
int lca=Lca(x,y);
return deep[x]+deep[y]-2*deep[lca];
}
void insert(int &y,int x,int l,int r,int v){
y=++tt;ls[y]=ls[x],rs[y]=rs[x],size[y]=size[x]+1;
if(l==r) return;
int mid=(l+r)>>1;
if(v<=mid) insert(ls[y],ls[x],l,mid,v);
else insert(rs[y],rs[x],mid+1,r,v);
}
int query(int y,int x,int l,int r,int k){
if(l==r) return l;
int mid=(l+r)>>1;
if(size[ls[y]]-size[ls[x]]>=k) return query(ls[y],ls[x],l,mid,k);
else return query(rs[y],rs[x],mid+1,r,k-(size[ls[y]]-size[ls[x]]));
}
void build(){
fa[1]=1;dfs1(1,0);dfs2(1,1);
for(int i=1;i<=n;i++) insert(rt[i],rt[i-1],1,n,id[i]);
}
}tree;
struct date{
struct node{
ll l,r;int rt;
}xh[N];
struct pir{
int x,rt,pos;
};
int fa[N][20];
pir getpos(ll x){
int l=1,r=tot,ans;
while(l<=r){
int mid=(l+r)>>1;
if(x<=xh[mid].r) r=mid-1,ans=mid;
else l=mid+1;
}
int id=xh[ans].rt;
return (pir){tree.query(tree.rt[tree.ed[id]],tree.rt[tree.dfn[id]-1],1,n,x-xh[ans].l+1),xh[ans].rt,ans};
}
void add(ll x,ll y){
tot++;pir v=getpos(y);
xh[tot].l=sum+1,xh[tot].r=sum+tree.sz[x],xh[tot].rt=x;
sum+=tree.sz[x];
d[tot]=d[v.pos]+tree.dis(v.x,v.rt)+1;
dep[tot]=dep[v.pos]+1;
fa[tot][0]=v.pos;Father[tot]=v.x;
for(int j=1;j<=16;j++) fa[tot][j]=fa[fa[tot][j-1]][j-1];
}
int Lca(int u,int v){
if(dep[u]<dep[v]) swap(u,v);
for(int i=18;i>=0;i--)
if(dep[fa[u][i]]>=dep[v]) u=fa[u][i];
if(u==v) return u;
for(int i=18;i>=0;i--)
if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i];
return fa[u][0];
}
ll ask(ll x,ll y){
pir u=getpos(x),v=getpos(y);
if(dep[u.pos]<=dep[v.pos]) swap(u,v);
if(u.pos==v.pos) return tree.dis(u.x,v.x);
else{
int lca=Lca(u.pos,v.pos);
if(lca==v.pos){
int g=u.pos;
for(int i=18;i>=0;i--){
if(fa[g][i]&&dep[fa[g][i]]>dep[lca]) g=fa[g][i];
}
ll ret=d[u.pos]-d[g]+1+tree.dis(u.x,u.rt);
ret+=tree.dis(Father[g],v.x);
return ret;
}
else{
int p=u.pos,q=v.pos;
for(int i=18;i>=0;i--){
if(fa[p][i]&&dep[fa[p][i]]>dep[lca]) p=fa[p][i];
}
for(int i=18;i>=0;i--){
if(fa[q][i]&&dep[fa[q][i]]>dep[lca]) q=fa[q][i];
}
ll ret=d[u.pos]-d[p]+1+tree.dis(u.x,u.rt)+d[v.pos]-d[q]+1+tree.dis(v.x,v.rt);
ret+=tree.dis(Father[p],Father[q]);
return ret;
}
}
}
}super;
int main(){
scanf("%d%d%d",&n,&m,&Q);
for(int i=1;i<n;i++){
int x,y;scanf("%d%d",&x,&y);
lnk(x,y);
}
tree.build();tot=1;sum=n;dep[1]=1;super.fa[1][0]=1;
super.xh[1].l=1,super.xh[1].r=n,super.xh[1].rt=1;
for(int i=1;i<=m;i++){
ll x,y;scanf("%lld%lld",&x,&y);
super.add(x,y);
}
for(int i=1;i<=Q;i++){
ll x,y;scanf("%lld%lld",&x,&y);
printf("%lld\n",super.ask(x,y));
}
return 0;
}
bzoj 4539: [Hnoi2016]树的更多相关文章
- BZOJ 4539: [Hnoi2016]树 [主席树 lca]
4539: [Hnoi2016]树 题意:不想写.复制模板树的子树,查询两点间距离. *** 终于有一道会做的题了...... 画一画发现可以把每次复制的子树看成一个大点来建一棵树,两点的lca一定在 ...
- bzoj 4539 [Hnoi2016]树——主席树+倍增
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4539 明明就是把每次复制的一个子树当作一个点,这样能连出一个树的结构,自己竟然都没想到.思维 ...
- 4539: [Hnoi2016]树
4539: [Hnoi2016]树 链接 分析: 主席树+倍增. 代码: #include<cstdio> #include<algorithm> #include<cs ...
- [BZOJ4539][HNOI2016]树(主席树)
4539: [Hnoi2016]树 Time Limit: 40 Sec Memory Limit: 256 MBSubmit: 746 Solved: 292[Submit][Status][D ...
- 【LG3248】[HNOI2016]树
[LG3248][HNOI2016]树 题面 洛谷 题解 因为每次你加入的点是原树上某一棵子树 那么我们一次加入一个点,代表一棵子树加到大树下面 那么我们要找到一个点在一个大点中用主席树在\(dfs\ ...
- BZOJ 2243 染色 | 树链剖分模板题进阶版
BZOJ 2243 染色 | 树链剖分模板题进阶版 这道题呢~就是个带区间修改的树链剖分~ 如何区间修改?跟树链剖分的区间询问一个道理,再加上线段树的区间修改就好了. 这道题要注意的是,无论是线段树上 ...
- BZOJ.1036 [ZJOI2008]树的统计Count ( 点权树链剖分 线段树维护和与最值)
BZOJ.1036 [ZJOI2008]树的统计Count (树链剖分 线段树维护和与最值) 题意分析 (题目图片来自于 这里) 第一道树链剖分的题目,谈一下自己的理解. 树链剖分能解决的问题是,题目 ...
- [HNOI2016]树(可持久化线段树+树上倍增)
[HNOI2016]树(可持久化线段树+树上倍增) 题面 给出一棵n个点的模板树和大树,根为1,初始的时候大树和模板树相同.接下来操作m次,每次从模板树里取出一棵子树,把它作为新树里节点y的儿子.操作 ...
- [BZOJ 4771]七彩树(可持久化线段树+树上差分)
[BZOJ 4771]七彩树(可持久化线段树+树上差分) 题面 给定一棵n个点的有根树,编号依次为1到n,其中1号点是根节点.每个节点都被染上了某一种颜色,其中第i个节点的颜色为c[i].如果c[i] ...
随机推荐
- 【Java】java 中的泛型通配符——从“偷偷地”地改变集合元素说起
一直没注意这方面的内容,想来这也算是基础了,就写了这个笔记. 首先java的通配符共有三种----先别紧张,现在只是粗略的过一下,看不看其实无所谓 类型 介绍 <?> 无限定通配符,等价于 ...
- HTML5学习指导路线
HTML5是现在热门的技术,经过8年的艰苦努力,该标准规范终于制定完成,在这里为想要学习HTML5初级程序员详细划分一下学习内容和步骤,让大家清楚的知道HTML5需要学什么?能够快速掌握HTML5开发 ...
- 关于Could not resolve dependencies for project
异常:Could not resolve dependencies for project 思路:网上提出的方案思路都是把相互依赖的项目导入到本地仓库中. 目前一劳永逸的方法是:将<packag ...
- 变位词(0029)-swustoj
变位词(0029)水题 变位词如果两个单词的组成字母完全相同,只是字母的排列顺序不一样,则它们就是变位词,两个单词相同也被认为是变位词.如tea 与eat , nic 与cin, ddc与dcd, a ...
- Service 之间如何通信?- 每天5分钟玩转 Docker 容器技术(101)
微服务架构的应用由若干 service 组成.比如有运行 httpd 的 web 前端,有提供缓存的 memcached,有存放数据的 mysql,每一层都是 swarm 的一个 service,每个 ...
- Tinc VPN
服务端配置 安装 $ apt-get install tinc 配置 $ mkdir -p /etc/tinc/dock/hosts $ cd /etc/tinc/dock 配置 tinc.conf ...
- 深入理解ES6之——JS类的相关知识
基本的类声明 类声明以class关键字开始,其后是类的名称:剩余部分的语法看起来像对象字面量中的方法简写,并且在方法之间不需要使用逗号. class Person { //等价于prototype的构 ...
- ios2048小游戏
最近突然想写一个2048的小游戏,由于全部是自定义控件,所以程序看起来冗杂,但是核心的算法部分还是很不错的,大家感兴趣的可以仔细看看. 声明部分: #import <UIKit/UIKit.h& ...
- Akka(40): Http:Marshalling reviewed - 传输数据序列化重温
上篇我们讨论了Akka-http的文件交换.由于文件内容编码和传输线上数据表达型式皆为bytes,所以可以直接把文件内容存进HttpEntity中进行传递.那么对于在内存里自定义的高级数据类型则应该需 ...
- 逻辑运算&数据
数据在计算机中只是0和1而已 数据在我们的理论中可以无穷大,但是在计算机中并不是,毕竟硬盘是有大小的. 具体可以通过一张图来理解 例如,0-F的表示 上面是有符号数,那么无符号数则是 事实上,计算机中 ...