uoj#400. 【CTSC2018】暴力寫掛(邊分治)
阿新 • • 發佈:2019-01-14
做一道題學一堆東西.jpg
貓老師的題……暴力拿的分好像比打掛的正解多很多啊……我純暴力+部分分已經能有80了……正解沒調對之前一直只有10分→_→
先說一下什麼是邊分治。這個其實類似於點分治,不過分治物件從點換成邊了,就是每次找到一條邊,使其斷開之後的兩個連通塊中最大的最小
於是我們就可以……等會兒如果在菊花圖上怎麼辦?不是得卡到\(O(n^2)\)了?
不難發現這個東西的複雜度和節點的度數有關,於是為了假裝這個東西能用避免這些情況,我們要把圖給重構嘍
簡單來說就是通過加入虛點,把圖給搞成一棵二叉樹,這樣的話節點度數就小了,複雜度也沒問題了
原理大概就這樣,具體實現還是看程式碼比較好
然後回到本題
首先在第二棵樹上的\(LCA\)的深度它就不是個東西,我們只能去列舉它,那麼能選的點就是它的不同子樹中的點了
然後考慮轉化一下,\[dep(x) + dep(y) - dep(LCA(x,y))=\frac{1}{2}(dep(x) + dep(y) + dis(x,y))\]
然後我們就可以把它給轉化成一棵無根樹了
在分治過程中,對於每條邊\((i,j)\),要維護兩邊子樹中中最大的\(dep_u+dis_{i,u}\)和\(dep_v+dis_{j,v}\),然後用自己這條邊更新答案
然後因為邊分樹是一棵二叉樹,我們可以用線段樹來維護上面的資訊
然後就沒有然後了(就算有然後我也布吉島是怎麼回事)
//minamoto #include<bits/stdc++.h> #define R register #define ll long long #define inf 1e18 #define fp(i,a,b) for(R int i=a,I=b+1;i<I;++i) #define fd(i,a,b) for(R int i=a,I=b-1;i>I;--i) #define go(T,u) for(int i=T.head[u],v=T.e[i].v;i;i=T.e[i].nx,v=T.e[i].v) template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;} using namespace std; char buf[1<<21],*p1=buf,*p2=buf; inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;} int read(){ R int res,f=1;R char ch; while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1); for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0'); return res*f; } const int N=7.5e5+5; struct eg{int v,nx,w;}st[N]; struct Gr{ eg e[N<<1];int head[N],tot; inline void add(R int u,R int v,R int w){e[++tot]={v,head[u],w},head[u]=tot;} }T,G,H; ll toroot[N]; int n,cnt; void rebuild(int u,int fa){ int h=1,t=0; go(T,u)if(v!=fa)toroot[v]=toroot[u]+T.e[i].w,rebuild(v,u); go(T,u)if(v!=fa)st[++t]=T.e[i]; while(t-h>=2){ eg s1=st[h++],s2=st[h++]; int w=++cnt;st[++t]={w,0,0}; H.add(w,s1.v,s1.w),H.add(s1.v,w,s1.w); H.add(w,s2.v,s2.w),H.add(s2.v,w,s2.w); } while(h<=t)H.add(u,st[h].v,st[h].w),H.add(st[h].v,u,st[h].w),++h; } int ls[N<<1],rs[N<<1],fa[N<<1],dif[N],sz[N],dep[N],val[N<<1];ll dis[25][N]; int size; void getdis(int u,int fat,int dep){ go(H,u)if(v!=fat&&v!=-1){ dis[dep][v]=dis[dep][u]+H.e[i].w; getdis(v,u,dep); } } void getgr(int u,int fat,int &g1,int &g2){ sz[u]=1; go(H,u)if(v!=fat&&v!=-1){ getgr(v,u,g1,g2),sz[u]+=sz[v]; if(dif[g2]>dif[v])g1=u,g2=v; }dif[u]=abs(size-(sz[u]<<1)); } int gettr(int u,int dep,int s){ if(s==1)return ::dep[u]=dep,u; getdis(u,0,dep); int now=++cnt,g1=0,g2=0; size=s,getgr(u,0,g1,g2); go(H,g1)if(v==g2){val[now]=H.e[i].w,H.e[i].v=-1;break;} go(H,g2)if(v==g1){H.e[i].v=-1;break;} rs[now]=gettr(g1,dep+1,size-sz[g2]); ls[now]=gettr(g2,dep+1,sz[g2]); fa[ls[now]]=fa[rs[now]]=now; return now; } void init(){ cnt=n,rebuild(1,0); dif[0]=0x3f3f3f3f,gettr(1,0,cnt); } ll lv[N<<4],rv[N<<4],res; int id,mp[N<<4],rt[N],lp[N<<4],rp[N<<4]; int merge(int x,int y,ll d){ if(!x||!y)return x|y; cmax(res,((lv[x]+rv[y]+val[mp[x]])>>1)-d); cmax(res,((rv[x]+lv[y]+val[mp[x]])>>1)-d); cmax(lv[x],lv[y]),lp[x]=merge(lp[x],lp[y],d); cmax(rv[x],rv[y]),rp[x]=merge(rp[x],rp[y],d); return x; } int ins(int u){ for(int i=dep[u],x=u,las=0;i;--i,las=cnt,u=fa[u]){ mp[++cnt]=fa[u]; lv[cnt]=rv[cnt]=-inf; if(u==ls[fa[u]])cmax(lv[cnt],dis[i][x]+toroot[x]),lp[cnt]=las; else cmax(rv[cnt],dis[i][x]+toroot[x]),rp[cnt]=las; }return cnt; } void solve(int u,int fat,ll d){ rt[u]=ins(u),cmax(res,toroot[u]-d); go(G,u)if(v!=fat){ solve(v,u,d+G.e[i].w); rt[u]=merge(rt[u],rt[v],d); } } int u,v,w; int main(){ // freopen("testdata.in","r",stdin); n=read(); fp(i,1,n-1)u=read(),v=read(),w=read(),T.add(u,v,w),T.add(v,u,w); fp(i,1,n-1)u=read(),v=read(),w=read(),G.add(u,v,w),G.add(v,u,w); init(),solve(1,0,0); printf("%lld\n",res); return 0; }