1. 程式人生 > >2018.12.12【BZOJ5192】【洛谷P4271】New Barns(動態維護倍增陣列)

2018.12.12【BZOJ5192】【洛谷P4271】New Barns(動態維護倍增陣列)

BZOJ傳送門

洛谷傳送門


解析:

本來這道題ldw說是什麼動態點分治?

然後被我成功用動態維護LCA水過去了。

思路:

一看直徑,就是最遠距離,但是這個顯然用 O ( n ) O(n) 的DP每次來一下是不行的。

那麼重點就在距離上面,樹上距離可以用什麼東西維護?LCA+深度就可以 O ( log n ) O(\log n) 算出兩點距離。

所以我們只需要維護每棵樹的當前直徑的左右端點,新加入一個點的時候處理出它的倍增陣列,然後更新當前連通塊的直徑左右端點就行了,新的直徑的兩個端點必然有至少一個是原來直徑的端點。這個證明十分顯然,就不討論了。

對於詢問,直接查連通塊裡面到兩個直徑的距離就行了。


程式碼:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

namespace
IO{ inline int getint(){ re int num; re char c; re bool f=0; while(!isdigit(c=gc()))if(c=='-')f=1;num=c^48; while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48); return f?-num:num; } inline void outint(int a){ static char ch[23]; if(a==0)pc('0'); while(a)ch[++ch[0]]=a-a/10*10,a/=10; while(ch[0])pc(ch[ch[0]--]^48); } inline char getalpha(){ re char c; while(!isalpha(c=gc())); return c; } } using namespace IO; cs int N=100005,logN=17; int block[N],l[N],r[N],bcnt; int fa[N][logN+1],dep[N]={-1}; inline int dist(int u,int v){ re int ans=0; if(dep[u]>dep[v])swap(u,v); if(dep[u]<dep[v]) for(int re i=logN;~i;--i){ if(dep[fa[v][i]]>=dep[u]){ v=fa[v][i]; ans+=1<<i; } } if(v==u)return ans; for(int re i=logN;~i;--i){ if(fa[v][i]^fa[u][i]){ ans+=2<<i; u=fa[u][i]; v=fa[v][i]; } } return ans+2; } int Q,tot; signed main(){ Q=getint(); while(Q--){ re char op=getalpha(); re int u=getint(); switch(op){ case 'Q':{ int b=block[u]; int d1=dist(u,l[b]),d2=dist(u,r[b]); outint(max(d1,d2)); pc('\n'); break; } case 'B':{ ++tot; if(~u){ block[tot]=block[u]; dep[tot]=dep[u]+1; fa[tot][0]=u; for(int re i=1;i<=logN;++i) fa[tot][i]=fa[fa[tot][i-1]][i-1]; int b=block[u],d1=dist(tot,l[b]),d2=dist(l[b],r[b]); if(d1>d2)r[b]=tot; d1=dist(tot,r[b]); if(d1>d2)l[b]=tot; } else{ block[tot]=++bcnt; r[bcnt]=l[bcnt]=tot; } break; } } } return 0; }