一時興起回來寫個一篇 至於之後會不會繼續更新就再說吧
這題是第一次國培的回家作業其中一題(夭壽 上一篇文的時候我還沒進二階www)
重心剖分總之還是不太熟 寫了大概四個小時才寫掉
[Tree Coordinates](https://www.hackerrank.com/contests/world-codesprint-8/challenges/tree-coordinates/problem)
### 題目: 定義樹上的距離$d(x,y)$就是會經過的邊數, 那麼對於兩個點對$(x1,y1), (x2,y2)$的距離就是$d(x1,x2)+d(y1,y2)$。 給定一棵樹跟一堆點對,求所有點對對當中距離最遠的那個,輸出距離。
樹上點數$N \leq 75000$,點對數量$M \leq 75000$。
### 不負責題解(?):
那麼對於每個點對$(x,y)$,把y紀錄在x,接下來遍歷一次原樹,在喵點的時候就把所有點對對的x座標的lca為喵點的點對對做過一次(饒舌)
具體來講,我們在增加一個點對的時候,可以在重心樹上y的位置插入一個$dep[x]$的值,在查詢時就可以直接在重心樹上查離查詢的y距離最大的那個值了(這個距離包含樹上距離跟點值的加權)。不過要注意,為了避免查詢到lca實際上小於自己的點對的答案,merge子樹時要query完一整顆子樹再一次加進去。
另外在重心樹上查詢的時候,可能會需要(至少我要)掃過所有的子樹,這樣如果有太陽就會炸的很慘,所以要建虛點讓每個點的度數保持在定值下。
上code OwO
#define up(x,y) (x=max(x,y)) vector<int> qy[235005],wk,cG[235005]; vector<pair<int,int>> G[235005]; int sz[235005],mxch[235005],ctpa[235005],dep[235005],dis[235005],last[235005],dtc[16][235005],ictd[235005],allval[235005],myval[235005],tpval[235005]; bitset<235005> inct; void dfs1(int now,int pa){ wk.pb(now); for(pair<int,int> p:G[now]){ if(inct[p.first] || p.first==pa)continue; dis[p.first]=dis[now]+p.second; dfs1(p.first,now); sz[now]+=sz[p.first]; mxch[now]=max(mxch[now],sz[p.first]); } ++sz[now]; } void dfs1_5(int now,int pa,int dep,int nowd){ dtc[dep][now]=nowd; for(pair<int,int> p:G[now]){ if(p.first==pa)continue; if(inct[p.first])continue; dfs1_5(p.first,now,dep,nowd+p.second); } } int cd(int now,int pa,int dep){ dfs1(now,now); int cen=-1; for(int i:wk)if(max(mxch[i],int(wk.size())-sz[i])<=int(wk.size())/2)cen=i; dfs1_5(cen,cen,dep,0); ictd[cen]=dep; ctpa[cen]=pa; inct[cen]=1; for(int i:wk){ sz[i]=mxch[i]=0; } wk.clear(); for(pair<int,int> p:G[cen]){ if(inct[p.first])continue; dis[p.first]=p.second; int chcen=cd(p.first,cen,dep+1); cG[cen].pb(chcen); } return cen; } void dfs2(int now,int pa){ for(pair<int,int> p:G[now]){ if(p.first==pa)continue; dep[p.first]=dep[now]+p.second; dfs2(p.first,now); sz[now]+=sz[p.first]; } sz[now]+=qy[now].size(); } set<int> _add_modify_point; void clearct(){ for(int i:_add_modify_point){ allval[i]=-999999; tpval[i]=-999999; myval[i]=-999999; } _add_modify_point.clear(); } void add(int x,int v){ int ox=x,prv=x; up(myval[x],v); x=ctpa[x]; while(x){ up(tpval[prv],v+dtc[ictd[x]][ox]); up(allval[x],v+dtc[ictd[x]][ox]); _add_modify_point.insert(x); _add_modify_point.insert(prv); prv=x; x=ctpa[x]; } } int query(int x,int v=-999999){ int prv=x,ox=x; v=max(v,allval[x]); v=max(v,myval[x]); x=ctpa[x]; while(x){ for(int cch:cG[x]){ if(cch==prv)continue; v=max(v,dtc[ictd[x]][ox]+tpval[cch]); } v=max(v,dtc[ictd[x]][ox]+myval[x]); prv=x; x=ctpa[x]; } return v; } int ans; vector<pair<int,int>> *dfs3(int now,int pa,bool clean){ sort(G[now].begin(),G[now].end(),[&](const pair<int,int> &a,const pair<int,int> &b){ if(b.first==pa)return true; if(a.first==pa)return false; return sz[a.first]<sz[b.first]; }); vector<vector<pair<int,int>>*> rec; for(pair<int,int> p:G[now]){ if(p.first==pa){ __is_end:; vector<pair<int,int>>* rt; if(rec.size()){ rt=rec.back(); rec.pop_back(); } else rt=new vector<pair<int,int>>(); for(auto *v:rec){ for(auto &p:*v){ int Q=query(p.second); ans=max(ans,Q+dep[p.first]-dep[now]*2); } for(auto &p:*v){ add(p.second,dep[p.first]); } } for(auto &sec:qy[now]){ int Q=query(sec); ans=max(ans,Q-dep[now]); } for(auto &sec:qy[now]){ add(sec,dep[now]); } for(auto *v:rec){ if(v->size()>rt->size())swap(rt,v); for(auto &p:*v)rt->pb(p); delete v; } for(auto &sec:qy[now]){ rt->eb(now,sec); } return rt; } else{ clearct(); vector<pair<int,int>> *rt=dfs3(p.first,now,p!=G[now].back()); rec.pb(rt); if(p==G[now].back())goto __is_end; } } assert(0); } int main(){ CPPinput; int n,m; cin>>n>>m; for(int i=1;i<=n;++i)last[i]=i; int N=n; for(int i=1,u,v;i<n;++i){ cin>>u>>v; int nu=++N,nv=++N; G[last[u]].eb(nu,0); G[last[v]].eb(nv,0); G[nu].eb(last[u],0); G[nv].eb(last[v],0); G[nu].eb(nv,1); G[nv].eb(nu,1); last[u]=nu, last[v]=nv; } int root=cd(1,0,1); while(m--){ int x,y; cin>>x>>y; qy[x].pb(y); } dep[1]=1; dfs2(1,1); dfs3(1,1,0); cout<<ans<<endl; }順便還花了一個小時在搞新的style.... 看起來總算是值得了(?
沒有留言:
張貼留言