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

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=100100,M=2000100;
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=0; !isdigit(ch=getchar()); t=(ch==‘-‘));
   for (x=ch-‘0‘; isdigit(ch=getchar()); x=x*10+ch-‘0‘);
   if (t) x=-x;
}

void ins(int x,int &y,int L,int R,int k){
   y=++tot; sm[y]=sm[x]+1;
   if (L==R) return; int mid=(L+R)>>1;
   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+1,R,k);
}

int que(int k,int z){
   int L=1,R=n,mid,t,x=rt[lf[k]-1],y=rt[rg[k]];
   while (L<R){
      mid=(L+R)>>1; t=sm[ls[y]]-sm[ls[x]];
      if (z<=t) R=mid,x=ls[x],y=ls[y];
         else L=mid+1,z-=t,x=rs[x],y=rs[y];
   }
   return L;
}

int getid(ll x,int ed){
   int L=1,R=ed+1,mid;
   while (L+1<R){
      mid=(L+R)>>1;
      if (a[mid].l<=x) L=mid; else R=mid;
   }
   return L;
}

struct T{
   int tot,fst[N],pnt[N<<1],len[N<<1],nxt[N<<1];
   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]=1;
      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,1,n) ins(1,n,rt[i-1],rt[i],id[i]);
   }

   ll dist(int x,int y){ return d[x]+d[y]-(d[lca(x,y)]<<1); }
}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,1,n-1) rd(x),rd(y),t1.add(x,y,1),t1.add(y,x,1);
   t1.dfs(1); t1.nbr(1,1);
   rep(i,1,n) ins(rt[i-1],rt[i],1,n,id[i]);
   a[1].id=1; a[1].rt=1; a[1].l=1; a[1].r=n;
   rep(i,1,m){
      rd(x); rd(y);
      a[i+1].rt=x; a[i+1].id=i+1;
      a[i+1].l=a[i].r+1; a[i+1].r=a[i].r+t1.sz[x];
      z=getid(y,i); a[i+1].fa=y=que(a[z].rt,y-a[z].l+1);
      t2.add(z,i+1,t1.d[y]-t1.d[a[z].rt]+1);
   }
   t2.dfs(1); t2.div(1,1); int u,v,w; ll ans;
   while (cas--){
      rd(x); rd(y); u=getid(x,m+1); v=getid(y,m+1); w=t2.lca(u,v);
      x=que(a[u].rt,x-a[u].l+1); y=que(a[v].rt,y-a[v].l+1);
      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)+1;
         }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])<<1;
         }
         printf("%lld\n",ans);
      }
   }
   return 0;
}

原文地址:https://www.cnblogs.com/HocRiser/p/8318861.html

时间: 2024-10-12 15:30:02

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

hdu 2665 可持久化线段树求区间第K大值(函数式线段树||主席树)

http://acm.hdu.edu.cn/showproblem.php?pid=2665 Problem Description Give you a sequence and ask you the kth big number of a inteval. Input The first line is the number of the test cases. For each test case, the first line contain two integer n and m (

可持久化线段树--主席树

浅谈可持久化线段树--主席树 权值线段树 权值线段树和普通线段树不一样的地方就是在于 它的结点存储的是区间内数的个数 这个线段树的好处就在于我们可以根据 左子树 和 右子树 的大小从而进行 查找某个数的排名 或者 查找排名为rk的数 可持久化的含义 可持久数据结构主要指的是我们可以查询历史版本的情况并支持插入,利用使用之前历史版本的数据结构来减少对空间的消耗(能够对历史进行修改的是函数式). 主席树的建树过程: 最开始的时候就是一个空树 然后我们再插入一个元素3 再加入一个元素 1 模版一 :求

小结:线段树 &amp; 主席树

概要: 就是用来维护区间信息,然后各种秀智商游戏. 应用: 优化dp.主席树等. 技巧及注意: size值的活用:主席树就是这样来的.支持区间加减,例题和模板:主席树,[BZOJ]1146: [CTSC2008]网络管理Network(树链剖分+线段树套平衡树+二分 / dfs序+树状数组+主席树),[BZOJ]1901: Zju2112 Dynamic Rankings(区间第k小+树状数组套可持久化线段树(主席树)) 01(就是更新和不更新等这种对立操作)情况:我们就要在各个更新的操作中明白

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

Description 背景 想Kpm当年为了防止别人随便进入他的MC,给他的PC设了各种奇怪的密码和验证问题(不要问我他是怎么设的...),于是乎,他现在理所当然地忘记了密码,只能来解答那些神奇的身份验证问题了... 描述 Kpm当年设下的问题是这样的: 现在定义这么一个概念,如果字符串s是字符串c的一个后缀,那么我们称c是s的一个kpm串. 系统将随机生成n个由a…z组成的字符串,由1…n编号(s1,s2…,sn),然后将它们按序告诉你,接下来会给你n个数字,分别为k1…kn,对于每 一个k

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

3545: [ONTAK2010]Peaks Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 635  Solved: 177[Submit][Status] Description 在Bytemountains有N座山峰,每座山峰有他的高度h_i.有些山峰之间有双向道路相连,共M条路径,每条路径有一个困难值,这个值越大表示越难走,现在有Q组询问,每组询问询问从点v开始只经过困难值小于等于x的路径所能到达的山峰中第k高的山峰,如果无解输出-1. I

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

原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ218.html 题解 如果我们可以知道每次弹出栈之后新的栈顶是什么,那么我们就可以在一棵区间覆盖.区间求和的线段树上完成这个问题. 于是本题的重点转到了如何求新的栈顶. 考虑用一个主席树维护一下每一个时刻每一个位置的栈顶元素的进栈时间,那么新的栈顶就是 当前位置栈顶的进栈时间-1 这时候的栈顶元素,然后这个东西也可以用我们维护的进栈时间来得到,所以我们只需要弄一个支持区间覆盖单点查询历史版本的主席树:这

4771: 七彩树 主席树

题意:给定一棵树  每个结点有一个颜色 然后又m个询问 询问:x d   问x的子树内不超过dep[x]+d 深度的子树结点一共有多少个颜色? 1.可以先将问题简化为问整个子树内有多少个不同的颜色  暴力解法树套树  但是可以用一个技巧来快速维护: 一个颜色一个颜色地处理  把所有相同颜色的点按照dfs序排序,每个点给自己的位置贡献1,相邻的两个点给lca贡献−1.然后只要区间内存在这种颜色,则其子树内的权值和必定为1.那么只需要这样子染好所有颜色之后询问子树和. 所以如果问题是这样的话只要一个

权值线段树&amp;&amp;可持久化线段树&amp;&amp;主席树

权值线段树 顾名思义,就是以权值为下标建立的线段树. 现在让我们来考虑考虑上面那句话的产生的三个小问题: 1. 如果说权值作为下标了,那这颗线段树里存什么呢? ----- 这颗线段树中, 记录每个值出现的次数 2.权值很大怎么办?数组空间不够啊 ----- 可以先离散化,再记录 3.那权值线段树到底是用来干嘛的呢? ----- 可以快速求出第k小值(其实主要还是为了主席树做铺垫啦) 那第k小值该怎么求呢??? 从树根依次往下 若当前值K大于左儿子的值,则将K-=左儿子的值,然后访问右儿子 若当前

HDU 5790 Prefix(字典树+主席树)

Prefix Time Limit: 2000/4000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 858    Accepted Submission(s): 256 Problem Description Alice gets N strings. Now she has Q questions to ask you. For each question, she wa