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] ...
随机推荐
- Ceph编译安装教程
Ceph官方版本目前支持的纠删码很有限,实验室这块希望能够整合我们自主开发的纠删码BRS(Binary Reed–Solomon encoding),所以需要编译Ceph环境.Ceph官方目前推荐的安 ...
- 简单模板view调用
$this->display();//模板名称与当前控制器.当前操作方法的名称不一致 $this->display();//调用当前控制器对应目录指定名称的模板 $this->dis ...
- Numpy入门 - 行列式转置
本章实例讲解如何将一个数组对象进行转置,我们可以使用.T将行列数据颠倒过来,看下面的代码: import numpy as np arr = np.array([[1, 2, 3], [4, 5, 6 ...
- 微信小程序爬坑日记
新公司上手小程序.30天,从入门到现在,还没放弃... 虽然小程序发布出来快一年了,爬坑的兄弟们大多把坑都踩平了.而我一直停留在"Hello World"的学习阶段.一来没项目,只 ...
- CCF-201503-2-数字排序
问题描述 试题编号: 201503-2 试题名称: 数字排序 时间限制: 1.0s 内存限制: 256.0MB 问题描述: 问题描述 给定n个整数,请统计出每个整数出现的次数,按出现次数从多到少的顺序 ...
- PHP 字符串转 bigint 型md5
1 2 3 4 5 6 7 8 /** * 字符串转bigint * @return bigint(string) */ public function md5( ...
- Solr6.1.0Windows安装步骤
一. 环境 solr 6.1.0 下载地址 http://archive.apache.org/dist/lucene/solr/6.1.0/ jdk 1.8 tomcat8 二. 安装solr到t ...
- windows10版本1709 在桌面和文件中点击右键,会引起卡顿
windows自动升级到1709版本后出现的问题,而之前是没有这种问题的. 最终解决办法:(需要设置注册表) 运行:快捷键Win+R regedit 路径:可直接复制后粘贴+回车 HKEY_CLASS ...
- 小程序使用wx.chooseAddress获取用户手机号码,微信chooseAddress接口获取用户收货信息
通常用户在商城购买产品后,需要填写他的收货信息,方便我们发货,但是在手机上写字非常不方便,一个客户的收货信息包括:姓名,地址和手机号码这些内容全部填写的话,至少要写20个字. 地址 所以有些客户在手机 ...
- 【MySQL】(4)操作数据表中的记录
1. 插入记录INSERT 方法一: INSERT [INTO] tbl_name [(clo_name,...)] {VALUES | VALUE} ({expr | DEFAULT},...),( ...