1. 程式人生 > >[BZOJ4539][HNOI2016]樹(主席樹)

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

.get submit r+ page class open swa 執行 upload

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;
}

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