4539: [Hnoi2016]树

Time Limit: 40 Sec  Memory Limit: 256 MB
Submit: 746  Solved: 292
[Submit][Status][Discuss]

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

5 2 3
1 4
1 3
4 2
4 5
4 3
3 2
6 9
1 8
5 3

Sample Output

6
3
3

HINT

经过两次操作后,大树变成了下图所示的形状:

结点6到9之间经过了6条边,所以距离为6;类似地,结点1到8之间经过了3条边;结点5到3之间也经过了3条边。

Source

思维难度低,代码难度高。

直接上主席树即可。

代码用时:0.5h。抄一次代码,错误率还是比较低的,只有一个地方接口参数写反了。

 #include<cstdio>
#include<algorithm>
#include<iostream>
#define rep(i,l,r) for (int i=l; i<=r; i++)
typedef long long ll;
using namespace std; const int N=,M=;
int n,m,tot,dfn,sm[M],ls[M],rs[M],rt[N],lf[N],rg[N],id[N];
struct P{ int id,rt,fa; ll l,r; }a[N]; template<typename T>inline void rd(T &x){
int t; char ch;
for (t=; !isdigit(ch=getchar()); t=(ch=='-'));
for (x=ch-''; isdigit(ch=getchar()); x=x*+ch-'');
if (t) x=-x;
} void ins(int x,int &y,int L,int R,int k){
y=++tot; sm[y]=sm[x]+;
if (L==R) return; int mid=(L+R)>>;
if (k<=mid) rs[y]=rs[x],ins(ls[x],ls[y],L,mid,k);
else ls[y]=ls[x],ins(rs[x],rs[y],mid+,R,k);
} int que(int k,int z){
int L=,R=n,mid,t,x=rt[lf[k]-],y=rt[rg[k]];
while (L<R){
mid=(L+R)>>; t=sm[ls[y]]-sm[ls[x]];
if (z<=t) R=mid,x=ls[x],y=ls[y];
else L=mid+,z-=t,x=rs[x],y=rs[y];
}
return L;
} int getid(ll x,int ed){
int L=,R=ed+,mid;
while (L+<R){
mid=(L+R)>>;
if (a[mid].l<=x) L=mid; else R=mid;
}
return L;
} struct T{
int tot,fst[N],pnt[N<<],len[N<<],nxt[N<<];
int fa[N],sz[N],son[N],anc[N];
ll d[N];
void add(int x,int y,int z)
{ pnt[++tot]=y; len[tot]=z; nxt[tot]=fst[x]; fst[x]=tot; } void dfs(int x){
int p; sz[x]=;
for (p=fst[x]; p; p=nxt[p]){
int y=pnt[p];
if (y!=fa[x]){
fa[y]=x; d[y]=d[x]+len[p];
dfs(y); sz[x]+=sz[y];
if (sz[y]>sz[son[x]]) son[x]=y;
}
}
} void nbr(int x,int tp){
lf[x]=rg[x]=++dfn; id[dfn]=x; anc[x]=tp; int p;
if (son[x]) nbr(son[x],tp),rg[x]=rg[son[x]];
for (p=fst[x]; p; p=nxt[p]){
int y=pnt[p];
if (y!=fa[x] && y!=son[x]) nbr(y,y),rg[x]=rg[y];
}
} void div(int x,int tp){
anc[x]=tp; int p;
if (son[x]) div(son[x],tp);
for (p=fst[x]; p; p=nxt[p]){
int y=pnt[p];
if (y!=fa[x] && y!=son[x]) div(y,y);
}
} int lca(int x,int y){
for (; anc[x]!=anc[y]; x=fa[anc[x]])
if (d[anc[x]]<d[anc[y]]) swap(x,y);
return (d[x]<d[y]) ? x : y;
} int gettp(int x,int y){
int z;
for (; anc[x]!=anc[y]; x=fa[anc[x]]) z=anc[x];
return (x==y) ? z : son[y];
} void build(){
rep(i,,n) ins(,n,rt[i-],rt[i],id[i]);
} ll dist(int x,int y){ return d[x]+d[y]-(d[lca(x,y)]<<); }
}t1,t2; int main(){
freopen("bzoj4539.in","r",stdin);
freopen("bzoj4539.out","w",stdout);
rd(n); rd(m); int cas,z; ll x,y;
rd(cas);
rep(i,,n-) rd(x),rd(y),t1.add(x,y,),t1.add(y,x,);
t1.dfs(); t1.nbr(,);
rep(i,,n) ins(rt[i-],rt[i],,n,id[i]);
a[].id=; a[].rt=; a[].l=; a[].r=n;
rep(i,,m){
rd(x); rd(y);
a[i+].rt=x; a[i+].id=i+;
a[i+].l=a[i].r+; a[i+].r=a[i].r+t1.sz[x];
z=getid(y,i); a[i+].fa=y=que(a[z].rt,y-a[z].l+);
t2.add(z,i+,t1.d[y]-t1.d[a[z].rt]+);
}
t2.dfs(); t2.div(,); int u,v,w; ll ans;
while (cas--){
rd(x); rd(y); u=getid(x,m+); v=getid(y,m+); w=t2.lca(u,v);
x=que(a[u].rt,x-a[u].l+); y=que(a[v].rt,y-a[v].l+);
if (u==v) printf("%lld\n",t1.dist(x,y));
else{
if (u==w) swap(u,v),swap(x,y);
if (v==w){
v=t2.gettp(u,w);
ans=t1.d[x]-t1.d[a[u].rt]+t2.d[u]-t2.d[v];
x=a[v].fa; ans+=t1.dist(x,y)+;
}else{
ans=t1.d[x]-t1.d[a[u].rt]+t1.d[y]-t1.d[a[v].rt]+t2.dist(u,v);
u=t2.gettp(u,w); v=t2.gettp(v,w);
x=a[u].fa; y=a[v].fa;
ans-=(t1.d[t1.lca(x,y)]-t1.d[a[w].rt])<<;
}
printf("%lld\n",ans);
}
}
return ;
}

[BZOJ4539][HNOI2016]树(主席树)的更多相关文章

  1. 线段树简单入门 (含普通线段树, zkw线段树, 主席树)

    线段树简单入门 递归版线段树 线段树的定义 线段树, 顾名思义, 就是每个节点表示一个区间. 线段树通常维护一些区间的值, 例如区间和. 比如, 上图 \([2, 5]\) 区间的和, 为以下区间的和 ...

  2. BZOJ 4539: [Hnoi2016]树 [主席树 lca]

    4539: [Hnoi2016]树 题意:不想写.复制模板树的子树,查询两点间距离. *** 终于有一道会做的题了...... 画一画发现可以把每次复制的子树看成一个大点来建一棵树,两点的lca一定在 ...

  3. bzoj 4539 [Hnoi2016]树——主席树+倍增

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4539 明明就是把每次复制的一个子树当作一个点,这样能连出一个树的结构,自己竟然都没想到.思维 ...

  4. 【BZOJ3439】Kpm的MC密码 trie树+主席树

    Description 背景 想Kpm当年为了防止别人随便进入他的MC,给他的PC设了各种奇怪的密码和验证问题(不要问我他是怎么设的...),于是乎,他现在理所当然地忘记了密码,只能来解答那些神奇的身 ...

  5. 学习笔记--函数式线段树(主席树)(动态维护第K极值(树状数组套主席树))

    函数式线段树..资瓷 区间第K极值查询 似乎不过似乎划分树的效率更优于它,但是如果主席树套树状数组后,可以处理动态的第K极值.即资瓷插入删除,划分树则不同- 那么原理也比较易懂: 建造一棵线段树(权值 ...

  6. HDU5790 Prefix 字典树+主席树

    分析:这个题和spoj的d_query是一个题,那个是求一段区间里有多少个不同的数字,这里是统计有多少个不同的前缀 用字典树进行判重,(和查询不同的数字一样)对于每个不同的前缀,只保留它最后一次出现的 ...

  7. bzoj 3545&&3551: [ONTAK2010]Peaks &&加强版 平衡树&&并查集合并树&&主席树

    3545: [ONTAK2010]Peaks Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 635  Solved: 177[Submit][Stat ...

  8. 线段树(单标记+离散化+扫描线+双标记)+zkw线段树+权值线段树+主席树及一些例题

    “队列进出图上的方向 线段树区间修改求出总量 可持久留下的迹象 我们 俯身欣赏” ----<膜你抄>     线段树很早就会写了,但一直没有总结,所以偶尔重写又会懵逼,所以还是要总结一下. ...

  9. UOJ#218. 【UNR #1】火车管理 线段树 主席树

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ218.html 题解 如果我们可以知道每次弹出栈之后新的栈顶是什么,那么我们就可以在一棵区间覆盖.区间求和 ...

随机推荐

  1. 用Matlab实现字符串分割(split)

    用Matlab实现字符串分割(split)Posted on 2011/08/08 Matlab的字符串处理没有C#强大,本身又没有提供OO特性,需要依赖别的手段完成这项任务. 我们在这里借助正则表达 ...

  2. 【转载】iPhone系统概览

    iPhone OS OverviewiPhone系统概览iPhone OS comprises the operating system and technologies that you use t ...

  3. 登入时session的处理方式

    暂时理解不够彻底  有空在详细介绍,先记录代码 1:创建一个工具类  存取当前登录用户 package com.liveyc.eloan.util; import javax.servlet.http ...

  4. 伪ajax操作

    什么是伪Ajax操作? 说白了就是假的ajax操作,但是它和正常的ajax操作的目的是一样的,把前端的信息发送到后台 先看一下代码吧! ajax.html <form action=" ...

  5. VC孙鑫老师第八课:你能捉到我吗?

    第一步,首先在对话框窗口上放上两个一模一样的按钮控件 第二步,由于是按钮响应鼠标移动上去的事件,因此需要重新派生按钮类: 第三步,在窗口类中声明并使用自定义按钮对象(记得在窗口类中包含自定义按钮类的头 ...

  6. 可能是是最全的Springboot基础视频分享,告别无视频可学

    一头扎进SpringBoot视频教程 SpringBoot入门 2017年-张志君老师-SpringBoot(新增) 欢迎关注我的微信公众号:"Java面试通关手册" 回复关键字& ...

  7. discuz各个目录与文件的作用说明

    discuz下面有很多文件夹以及文件,你们都知道他们是做什么的么?肯定不知道了吧.但是我们有经常遇到这些文件,譬如在后台文件校验操作都遇到某些文件被修改,这时候也需要知道这些文件是有什么作用的.今天就 ...

  8. jplayer.js 与 video.js

    HTML5 - 两款基于JS的视频播放器 都是基于h5 video 标签,如果不支持则会自动转成flash,这里个人比较推荐使用jplayer; 1.video.js pc端有时候会与视频打交道,如果 ...

  9. 如何使用curl命令指定ip访问url

    有时我们需要测试一个url,但域名并没解析,这时为了一个简单的测试而写host或去做域名解析,显然这并不高效,而有些域名甚至是正式的域名,因此我们可有使用curl命令进行测试 方法一 curl url ...

  10. [How to] 使用HBase协处理器---基本概念和regionObserver的简单实现

    1. 简介 对于HBase的协处理器概念可由其官方博文了解:https://blogs.apache.org/hbase/entry/coprocessor_introduction 总体来说其包含两 ...