题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4539

明明就是把每次复制的一个子树当作一个点,这样能连出一个树的结构,自己竟然都没想到。思维有待加强。

找编号为 k 的点,可以通过给 dfs 序建立对于编号的主席树。可以做一个 s[ i ] 表示第 i 步操作之后一共有多少个点,二分得知编号第 k 大的点在哪一步操作建出的大点里,然后用主席树查一下具体是哪个小点即可。每个大点记录一下自己的根,还有连向父亲中的哪个小点。

处理出每个小点在原树种的倍增数组和每个大点在新树中的倍增数组,就能查距离了。每个询问用刚才的二分和主席树找到询问的是哪个大点中的哪个小点。

在大点上倍增的时候先别跳最后一步,看看跳了之后是不是在同一个大点里,如果是,直接查询小点之间的距离即可。

数组开 n*17 好像有些不够。 n*20 可以。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ls Ls[cr]
#define rs Rs[cr]
#define ll long long
using namespace std;
ll rdn()
{
ll ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return fx?ret:-ret;
}
const int N=1e5+,K=,M=2e6+;//,M=N*K;
int n,m,Q,hd[N],xnt,to[N<<],nxt[N<<],pre[N][K+],dep[N],siz[N];
int h2[N],xt2,t2[N<<],nt2[N<<],pr2[N][K+],dp2[N];
int tim,dfn[N],rt[N],tot,Ls[M],Rs[M],sm[M];
ll s[N],dis[N][K+]; int bin[K+];
struct Node{
int x,y;
Node(int x=,int y=):x(x),y(y) {}
}a[N];
void add(int x,int y){to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;}
void ad2(int x,int y){t2[++xt2]=y;nt2[xt2]=h2[x];h2[x]=xt2;}
void ins(int l,int r,int &cr,int pr,int k)
{
cr=++tot; ls=Ls[pr]; rs=Rs[pr]; sm[cr]=sm[pr]+;
if(l==r)return; int mid=l+r>>;
if(k<=mid)ins(l,mid,ls,Ls[pr],k);
else ins(mid+,r,rs,Rs[pr],k);
}
int qry(int l,int r,int cr,int pr,int k)
{
if(l==r)return l; int mid=l+r>>;
int s=sm[ls]-sm[Ls[pr]];
if(s>=k)return qry(l,mid,ls,Ls[pr],k);
else return qry(mid+,r,rs,Rs[pr],k-s);
}
void ini_dfs(int cr,int fa)
{
siz[cr]=; dep[cr]=dep[fa]+;
dfn[cr]=++tim; ins(,n,rt[tim],rt[tim-],cr);
pre[cr][]=fa;
for(int t=,d;(d=pre[pre[cr][t-]][t-]);t++)
pre[cr][t]=d;
for(int i=hd[cr],v;i;i=nxt[i])
if((v=to[i])!=fa) ini_dfs(v,cr),siz[cr]+=siz[v];
}
int Dis(int x,int y)
{
if(!x||!y)return ; int ret=;//
for(int t=K,lm=dep[x];t>=;t--)
if(dep[pre[y][t]]>=lm)
ret+=bin[t], y=pre[y][t];
return ret;
}
void Ini_dfs(int cr,int fa)
{
dp2[cr]=dp2[fa]+; pr2[cr][]=fa;
dis[cr][]=Dis(a[fa].x,a[cr].y)+;
for(int t=,d;pr2[d=pr2[cr][t-]][t-];t++)
{
pr2[cr][t]=pr2[d][t-];
dis[cr][t]=dis[cr][t-]+dis[d][t-];
}
for(int i=h2[cr],v;i;i=nt2[i])
if((v=t2[i])!=fa) Ini_dfs(v,cr);
}
Node fnd(ll k,int r)
{
int l=,bh=;Node ret;
while(l<=r)
{
int mid=l+r>>;
if(s[mid]>=k)bh=mid,r=mid-;
else l=mid+;
}
ret.x=bh; k-=s[bh-]; bh=a[bh].x;
ret.y=qry(,n,rt[dfn[bh]+siz[bh]-],rt[dfn[bh]-],k);
return ret;
}
int Dis2(int x,int y)
{
int ret=;
if(dep[x]!=dep[y])
{
if(dep[x]<dep[y])swap(x,y);
for(int t=K,lm=dep[y];t>=;t--)
if(dep[pre[x][t]]>=lm)
ret+=bin[t], x=pre[x][t];
}
if(x!=y)
{
for(int t=K;t>=;t--)
if(pre[x][t]!=pre[y][t])
{ ret+=bin[t]<<; x=pre[x][t]; y=pre[y][t];}
ret+=;//
}
return ret;
}
int main()
{
n=rdn();m=rdn();Q=rdn(); ll u,v;
for(int i=;i<n;i++)
u=rdn(),v=rdn(),add(u,v),add(v,u);
bin[]=;for(int i=;i<=K;i++)bin[i]=bin[i-]<<;
ini_dfs(,); tot=;s[]=n;a[]=Node(,); m++;
for(int i=;i<=m;i++)
{
u=rdn();v=rdn();s[i]=s[i-]+siz[u];
Node bh=fnd(v,i-); a[i]=Node(u,bh.y);
ad2(i,bh.x); ad2(bh.x,i);
}
Ini_dfs(,);
for(int i=;i<=Q;i++)
{
u=rdn(); v=rdn(); ll ans=;
Node x=fnd(u,m), y=fnd(v,m);//*.x:blk, *.y:point
if(x.x==y.x){printf("%d\n",Dis2(x.y,y.y));continue;}
if(dp2[x.x]!=dp2[y.x])
{
if(dp2[x.x]<dp2[y.x])swap(x,y);
ans+=Dis(a[x.x].x,x.y);
for(int t=K,lm=dp2[y.x]+;t>=;t--)
if(dp2[pr2[x.x][t]]>=lm)
ans+=dis[x.x][t], x.x=pr2[x.x][t];
if(pr2[x.x][]==y.x)
{printf("%lld\n",ans+Dis2(y.y,a[x.x].y)+);continue;}
ans+=dis[x.x][]; x.x=pr2[x.x][]; x.y=a[x.x].x;
}
ans+=Dis(a[y.x].x,y.y); ans+=Dis(a[x.x].x,x.y);
for(int t=K;t>=;t--)
if(pr2[x.x][t]!=pr2[y.x][t])
{
ans+=dis[x.x][t]+dis[y.x][t];
x.x=pr2[x.x][t]; y.x=pr2[y.x][t];
}
printf("%lld\n",ans+Dis2(a[x.x].y,a[y.x].y)+);
}
return ;
}

bzoj 4539 [Hnoi2016]树——主席树+倍增的更多相关文章

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

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

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

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

  3. bzoj 4539: [Hnoi2016]树

    Description 小A想做一棵很大的树,但是他手上的材料有限,只好用点小技巧了.开始,小A只有一棵结点数为N的树,结 点的编号为1,2,-,N,其中结点1为根:我们称这颗树为模板树.小A决定通过 ...

  4. [BZOJ4539][HNOI2016]树(主席树)

    4539: [Hnoi2016]树 Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 746  Solved: 292[Submit][Status][D ...

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

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

  6. Bzoj 3123: [Sdoi2013]森林(主席树+启发式合并)

    3123: [Sdoi2013]森林 Time Limit: 20 Sec Memory Limit: 512 MB Description Input 第一行包含一个正整数testcase,表示当前 ...

  7. BZOJ 4556(后缀数组+主席树求前驱后继+二分||后缀数组+二分+可持久化线段树)

    换markdown写了.. 题意: 给你一个1e5的字符串,1e5组询问,求\([l_1,r_1]\)的所有子串与\([l_2,r_2]\)的lcp 思路: 首先可以发现答案是具有单调性的,我们考虑二 ...

  8. BZOJ 3524: [Poi2014]Couriers [主席树]

    3524: [Poi2014]Couriers Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 1892  Solved: 683[Submit][St ...

  9. bzoj 4012: [HNOI2015]开店 主席树

    Description 风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到 人生哲学.最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱.这样的 想法当然非常好啦,但是她们也发现 ...

随机推荐

  1. VAE demo

    先看tflearn 官方的: from __future__ import division, print_function, absolute_import import numpy as np i ...

  2. sql 判断字符串中是否含有数字和字母

    判断是否含有字母 select PATINDEX('%[A-Za-z]%', ‘ads23432’)=0 (如果存在字母,结果<>1) 判断是否含有数字 PATINDEX('%[0-9]% ...

  3. ZOJ 3822 Domination 概率dp 难度:0

    Domination Time Limit: 8 Seconds      Memory Limit: 131072 KB      Special Judge Edward is the headm ...

  4. poj1088 滑雪 解题报告

    滑雪 Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 77423   Accepted: 28779 Description ...

  5. harbor私有镜像仓库的搭建与使用与主从复制

    harbor私有镜像仓库,私有仓库有两种,一种是harbor,一种是小型的私有仓库,harbor有两种模式,一种是主 从,一种是高可用仓库,项目需求,需要两台服务器,都有docker.ldap权限统一 ...

  6. 面试题21:包含min函数的栈

    题目:定义栈的数据结构,要求添加一个min函数,能够得到栈的最小元素.要求函数min.push以及pop的时间复杂度都是O(1). 分析:google的一道面试题.我看到这道题目时,第一反应就是每次p ...

  7. 使用Mockito时遇到的一些问题

    最近在使用Mockito时遇到了几个比较tricking的问题,在这里记录一下. 1.如果方法的参数或者返回类型是泛型通配符相关的(如<?>,<? extends XXX>), ...

  8. 【DevExpress v17.2新功能预告】DevExtreme ASP.NET MVC新的强类型HTML Helpers

    在ASP.NET MVC中构建视图时,强类型HTML helpers非常有用.像@Html.TextBoxFor(m => m.FirstName)这样内置的Helper方法已经存在很长时间了, ...

  9. linux下挂盘

    1.首先,查看磁盘,fdisk -l Disk /dev/xvdf: bytes, sectors Units = sectors of * = bytes Sector size (logical/ ...

  10. FDMemTable.Delta 转SQL语句脚本

    {*******************************************************} { } { XE7.XE8.XE10 安卓 IOS 框架 } { } { 版权所有 ...