我寫的作法跟這篇寫的一樣,這裡大概提一下實作的方法跟證明。
首先當然是找出重心,然後預處理出每個點往下走到葉子的最長距離,還有他應該要往哪個子節點走才可以走到最遠的葉子。這樣就可以用一個 priority_queue 來處理「一直把路徑拔掉」的操作了。具體來說,priority_queue 裡面放的是所有現在候選的鍊的深度最淺的點,還有這條鍊的長度,當我們把一條鍊從 priority_queue 裡面拿出來時,就沿著這條鍊走下去,並且對於走到的每一個點 $X$ ,假設他沿著鍊往下走的點為 $Y$ ,那麼就把 $X$ 除了 $Y$ 以外的所有子節點 $Z$ 都加入 priority_queue 中,並且可以知道 $Z$ 所代表的鍊長為 $d[ Z ] + dis[ X ] [ Z ]$ ,其中 $d$ 代表每個點往下走到葉子的最長距離, $dis$ 則是兩點之間的距離。而再處理詢問時,我們還要知道前 $2y$ 條鍊的總長度是多少,所以會需要一個前綴和陣列,還有一個點是落在第幾條鍊上。另外當詢問的 $x$ 沒有落在前 $2y$ 條鍊上時,會必須要沿著 $x$ 往上走,走到第一個落在前 $2y$ 條鍊上的點,而這個部份就可以用類似在找 LCA 的倍增法處理,所以要先預處理好每個點的 $2^k$ 級祖先是誰。
至於證明,當前 $2y$ 條鍊已經包含 $x$ 了的話是顯然的,以下只考慮不包含 $x$ 的情形。事實上對於一次的詢問,有一個貪心的算法是:以 $x$ 為根建出這棵樹,然後依次取 $2y$ 次葉節點,使得每次多取一個葉節點時可以讓總長增加的最多,這樣會是最好的選法。但是當我們所有選的鍊都是落在 $x$ 的同一個子節點 $y$ 底下時,這樣就會變成總共有 $2y + 1$ 個葉節點,不符合要求,因此必須把其中一條捨棄掉,加入一條 $x$ 往 $y$ 以外的子節點連過去並且連到最遠葉子的路徑。那麼回到現在這張以重心為根的圖,會得到以 $x$ 為根時貪心所選的 $2y$ 條鍊都會是往 $x$ 的祖先的方向(否則以重心為根的前 $2y$ 條鍊會選到 $x$),所以必須捨棄掉其中一條,並加上 $d[ x ]$ ,這就和上面的算法一致了。
code :
#include<bits/stdc++.h> using namespace std; const int maxn=100000+10 ; struct P{int to,dis;}; vector<P> v[maxn] ; int n,anc[18][maxn],dis[maxn] ; void dfs(int x,int f,int d) { anc[0][x]=f ; dis[x]=d ; for(auto i : v[x]) if(i.to!=f) dfs(i.to,x,d+i.dis) ; } int get_cent() { dfs(1,1,0) ; int id,M=-1 ; for(int i=1;i<=n;i++) if(dis[i]>M) M=dis[id=i] ; dfs(id,id,0) ; int id2 ; M=-1 ; for(int i=1;i<=n;i++) if(dis[i]>M) M=dis[id2=i] ; int mi=M , cent ; for(int i=id2;i!=id;i=anc[0][i]) { int val=max(dis[i],M-dis[i]) ; if(val<=mi) mi=val , cent=i ; } return cent ; } int mson[maxn],mdis[maxn] ; void dfs2(int x,int f,int d) { anc[0][x]=f ; for(int i=1;i<18;i++) anc[i][x]=anc[i-1][anc[i-1][x]] ; dis[x]=d ; mson[x]=-1 ; mdis[x]=0 ; for(auto i : v[x]) if(i.to!=f) { dfs2(i.to,x,d+i.dis) ; if(i.dis+mdis[i.to]>mdis[x]) mdis[x]=i.dis+mdis[mson[x]=i.to] ; } } struct Q { int id,val ; bool operator < (const Q &rhs) const { return val<rhs.val ; } }; int chid[maxn],chcnt=0 ; int len[maxn] ; priority_queue<Q> pq ; void build(int root) { pq.push((Q){root,mdis[root]}) ; while(!pq.empty()) { Q u=pq.top() ; pq.pop() ; len[++chcnt]=u.val ; for(int i=u.id;i!=-1;i=mson[i]) { chid[i]=chcnt ; for(auto j : v[i]) if(j.to!=anc[0][i] && j.to!=mson[i]) pq.push((Q){j.to,j.dis+mdis[j.to]}) ; } } } int lsum[maxn] ; main() { int Q ; scanf("%d%d",&n,&Q) ; for(int i=1;i<n;i++) { int x,y,dis ; scanf("%d%d%d",&x,&y,&dis) ; v[x].push_back((P){y,dis}) ; v[y].push_back((P){x,dis}) ; } int root=get_cent() ; dfs2(root,root,0) ; build(root) ; for(int i=1;i<=chcnt;i++) lsum[i]=lsum[i-1]+len[i] ; for(int q=1,last=0;q<=Q;q++) { int x,y ; scanf("%d%d",&x,&y) ; if(q>1) x=(x+last-1)%n+1 , y=(y+last-1)%n+1 ; if(2*y>=chcnt) printf("%d\n",last=lsum[chcnt]) ; else if(chid[x]<=2*y) printf("%d\n",last=lsum[2*y]) ; else { int x2=x ; for(int i=17;i>=0;i--) if(chid[anc[i][x2]]>2*y) x2=anc[i][x2] ; x2=anc[0][x2] ; int ans=lsum[2*y]+mdis[x]+(dis[x]-dis[x2]) ; printf("%d\n",last=ans-min(len[2*y],mdis[x2])) ; } } }
沒有留言:
張貼留言