Luogu P3248 [HNOI2016]樹
阿新 • • 發佈:2020-10-07
來跟我一起說:陳指導是魔鬼,把一道我任務清單裡躺了兩年的題搬出來強制我寫了
但說實話可能昨天狀態挺好一下子就寫過了,沒有調成傻逼
但這題的思路其實很簡單,真·樹套樹即可
即每次進行復制操作時,將每次複製的子樹看做一個大點,這樣可以建立一個新的樹,我們稱為大樹
對於大樹上的點我們需要維護一些必要的資訊,比如這個某個節點\(i\)對應的小節點的編號區間\(L_i,R_i\),在模板樹上對應的複製點\(pre_i\),在大樹上懸掛在哪個小節點下面\(lst_i\)
根據這些我們可以完成一些操作:
getrt(x)
,表示找到小節點\(x\)對應的大樹上的節點是哪個,這個可以通過與\(L_i\)的大小關係來二分getpre(x)
,表示找到小節點\(x\)對應的模板樹上的節點是哪個,由於每次標號相當於一個求子樹第\(k\)大的過程,我們可以DFS序+主席樹,也可以線段樹合併
然後接下來的思路就是倍增了,對於大樹上的每個節點我們記錄倍增的祖先和距離,最後將維護下模板樹上的距離關係即可
說起來挺容易的但細節挺多,尤其是倍增求答案的時候有很多種小情況要注意
#include<cstdio> #include<iostream> #define RI register int #define CI const int& #define int long long using namespace std; const int N=100005,P=17; int n,m,q,x,y; namespace T1 //Template Tree { struct edge { int to,nxt; }e[N<<1]; int head[N],cnt,rt[N],size[N]; inline void addedge(CI x,CI y) { e[++cnt]=(edge){y,head[x]}; head[x]=cnt; e[++cnt]=(edge){x,head[y]}; head[y]=cnt; } class Segment_Tree { private: struct segment { int ch[2],size; }node[N*P<<2]; int tot; #define lc(x) node[x].ch[0] #define rc(x) node[x].ch[1] #define S(x) node[x].size #define TN CI l=1,CI r=n public: inline void modify(int& now,CI pos,TN) { now=++tot; ++S(now); if (l==r) return; int mid=l+r>>1; if (pos<=mid) modify(lc(now),pos,l,mid); else modify(rc(now),pos,mid+1,r); } inline int merge(CI x,CI y,TN) { if (!x||!y) return x|y; int now=++tot,mid=l+r>>1; S(now)=S(x)+S(y); lc(now)=merge(lc(x),lc(y),l,mid); rc(now)=merge(rc(x),rc(y),mid+1,r); return now; } inline int query(CI now,CI k,TN) { if (l==r) return l; int mid=l+r>>1; if (k<=S(lc(now))) return query(lc(now),k,l,mid); else return query(rc(now),k-S(lc(now)),mid+1,r); } #undef lc #undef rc #undef S #undef TN }SEG; #define to e[i].to class Tree_Distance_Solver { private: int anc[N][P],dep[N]; inline int getlca(int x,int y) { RI i; if (dep[x]<dep[y]) swap(x,y); for (i=P-1;~i;--i) if (dep[anc[x][i]]>=dep[y]) x=anc[x][i]; if (x==y) return x; for (i=P-1;~i;--i) if (anc[x][i]!=anc[y][i]) x=anc[x][i],y=anc[y][i]; return anc[x][0]; } public: inline void DFS(CI now=1,CI fa=0) { RI i; SEG.modify(rt[now],now); size[now]=1; for (anc[now][0]=fa,i=0;i<P-1;++i) if (anc[now][i]) anc[now][i+1]=anc[anc[now][i]][i]; else break; for (dep[now]=dep[fa]+1,i=head[now];i;i=e[i].nxt) if (to!=fa) DFS(to,now),size[now]+=size[to], rt[now]=SEG.merge(rt[now],rt[to]); } inline int getdis(CI x,CI y) { return dep[x]+dep[y]-(dep[getlca(x,y)]<<1LL); } }T; }; namespace T2 //Big Tree { int pre[N],lst[N],L[N],R[N],tot,num,anc[N][P],dep[N],dis[N][P]; inline int getrt(CI x) { int l=1,r=num,mid,ret; while (l<=r) if (L[mid=l+r>>1]<=x) ret=mid,l=mid+1; else r=mid-1; return ret; } inline int getpre(CI x) { int rt=getrt(x); return T1::SEG.query(T1::rt[pre[rt]],x-L[rt]+1); } inline void relink(CI x,CI y) { int rt=getrt(y); dep[++num]=dep[rt]+1; lst[num]=y; pre[num]=x; L[num]=tot+1; R[num]=tot+T1::size[x]; tot+=T1::size[x]; anc[num][0]=rt; dis[num][0]=T1::T.getdis(getpre(y),pre[rt])+1; for (RI i=0;i<P-1;++i) if (anc[num][i]) dis[num][i+1]=dis[num][i]+dis[anc[num][i]][i], anc[num][i+1]=anc[anc[num][i]][i]; else break; } inline int query(int x,int y,int ret=0) { int rx=getrt(x),ry=getrt(y); RI i; if (rx==ry) return T1::T.getdis(getpre(x),getpre(y)); if (dep[rx]<dep[ry]) swap(x,y),swap(rx,ry); ret+=T1::T.getdis(getpre(x),pre[rx]); x=rx; for (i=P-1;~i;--i) if (dep[anc[x][i]]>dep[ry]) ret+=dis[x][i],x=anc[x][i]; if (getrt(lst[x])==ry) return ret+1+T1::T.getdis(getpre(lst[x]),getpre(y)); ret+=T1::T.getdis(getpre(y),pre[ry]); y=ry; if (dep[x]>dep[y]) ret+=dis[x][0],x=anc[x][0]; for (i=P-1;~i;--i) if (anc[x][i]!=anc[y][i]) ret+=dis[x][i]+dis[y][i],x=anc[x][i],y=anc[y][i]; return ret+T1::T.getdis(getpre(lst[x]),getpre(lst[y]))+2; } }; signed main() { //reopen("treeman.in","r",stdin); freopen("treeman.out","w",stdout); RI i; for (scanf("%lld%lld%lld",&n,&m,&q),i=1;i<n;++i) scanf("%lld%lld",&x,&y),T1::addedge(x,y); T2::L[1]=T2::pre[1]=T2::num=1; T2::tot=T2::R[1]=n; for (T1::T.DFS(),i=1;i<=m;++i) scanf("%lld%lld",&x,&y),T2::relink(x,y); for (i=1;i<=q;++i) scanf("%lld%lld",&x,&y),printf("%lld\n",T2::query(x,y)); return 0; }