藍皮書:異象石 【dfs序+lca】
題目詳見藍皮書【演算法競賽:進階指南】。
題目大意:
就是給你一顆樹,然後我們要在上面進行三種操作:
1.標記某個點 或者 2.撤銷某個點的標記 以及 3.詢問標記點在樹上連通所需的最短總邊權
資料範圍:
點數以及運算元:1e5,邊權:1e9(意思就是答案要 long long 存)。
分析:
這道題比賽的時候看的是真懵逼。。。
表示只會 n^3 做法(最多會n^2),以及特殊形態(比如鏈或者菊花圖)的騙分法。
然鵝正解大概是 $O(n log n)$ 的做法,和樹搭上了關係,加上這資料範圍...
於是正解真是這個複雜度。(看到標程的時候表示驚訝,我太弱了)
標算就是用的dfs序加上lca的演算法(如題)
首先我們不考慮 2、3 操作,我們先考慮如果樹上有 k 個點被標記了,我們要得到樹上 k 個點連通圖的最小總邊權。
我們可以在紙上畫出這棵樹以及標記的點,然後我們從左到右把點連成一塊。
這時我們發現每兩個相鄰的點之間(相當於dfs序)的距離加上最後一個點和第一個點的距離,和正好是答案的兩倍。
也就是說,如果把這些標記點按照dfs序排成一列,首尾相連形成一個環的話,答案就是這個環相鄰點距離之和除以二。
(這種東西考場上怎麼做得出來嘛)
那麼我們回到原題,如果用上面的方法暴力處理答案,那麼複雜度是 $O(n^{2} log n)$ 的(還不如 我自己想到的 n^2 咧)。
那麼我們結合加點刪點特殊的性質,以此優化演算法複雜度。
我們發現加點其實就是在原環兩相鄰點之間插入了一個新點,然後原來兩個相鄰點的貢獻沒了,但是多了新點與這兩個點分別的貢獻。
那麼刪點類似的,就是令原環中某個點與相鄰的兩個點之間的貢獻刪除,並且多了這兩個點之間的距離的貢獻。
於是我們就可以用一個set來維護標記點,然後每次求距離的時候要用到 lca。
(lca建議常數小的樹剖,倍增效率感人,tarjan 的話比較冷門基本不考慮)
那麼我們就可以愉快地抄敲程式碼了!
程式碼 :
1 //by Judge 2 #include<bits/stdc++.h> 3 #pragma GCC optimize(2) 4 #define It set<int>::iterator 5 #define ll long long 6 using namespace std; 7 const int M=1e5+11; 8 #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) 9 char buf[1<<21],*p1,*p2; 10 inline int read(){ int x=0; 11 char c=getchar(); while(!isdigit(c)) c=getchar(); 12 for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x; 13 } inline int cread(){ char c=getchar(); 14 while(c!='+'&&c!='-'&&c!='?') c=getchar(); 15 return c=='?'?1:(c=='+'?2:3); 16 } char sr[1<<21],z[20];int C=-1,Z; 17 inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;} 18 inline void print(ll x,char chr='\n'){ 19 if(C>1<<20)Ot(); while(z[++Z]=x%10+48,x/=10); 20 while(sr[++C]=z[Z],--Z);sr[++C]=chr; 21 } int n,m,pat,tim,head[M],f[M],son[M]; ll ans,dis[M]; 22 int siz[M],top[M],dep[M],dfn[M],p[M]; set<int> s; 23 struct Edge{ int to,val,nxt; 24 Edge(int v,int c,int x):to(v),val(c),nxt(x){} Edge(){} 25 }e[M<<1]; 26 inline void add(int u,int v,int c){ 27 e[++pat]=Edge(v,c,head[u]),head[u]=pat; 28 e[++pat]=Edge(u,c,head[v]),head[v]=pat; 29 } 30 #define v e[i].to 31 void dfs(int u,int fa){ 32 siz[u]=1,f[u]=fa,dep[u]=dep[fa]+1; 33 for(int i=head[u];i;i=e[i].nxt) if(v^fa){ 34 dis[v]=dis[u]+e[i].val, 35 dfs(v,u),siz[u]+=siz[v]; 36 if(siz[v]>siz[son[u]]) son[u]=v; 37 } 38 } void dfs(int u){ 39 dfn[u]=++tim,p[tim]=u; 40 if(!top[u]) top[u]=u; if(!son[u]) return ; 41 top[son[u]]=top[u],dfs(son[u]); 42 for(int i=head[u];i;i=e[i].nxt) 43 if(v^f[u]&&v^son[u]) dfs(v); 44 } 45 #undef v 46 inline int lca(int u,int v){ 47 while(top[u]^top[v]) 48 (dep[top[u]]>dep[top[v]])? 49 u=f[top[u]]:v=f[top[v]]; 50 return dep[u]<dep[v]?u:v; 51 } inline ll get(int u,int v){ 52 return dis[u]+dis[v]-dis[lca(u,v)]*2; 53 } inline It L(It it){ 54 return (it==s.begin())?--s.end():--it; 55 } inline It R(It it){ 56 return (it==--s.end())?s.begin():++it; 57 } 58 int main(){ 59 n=read(); 60 for(int i=1,u,v,c;i<n;++i) 61 u=read(),v=read(), 62 c=read(),add(u,v,c); 63 dfs(1,0),dfs(1),m=read(); 64 for(int opt,x,t;m;--m){ 65 opt=cread(); It it; 66 if(opt==1) print(ans/2); 67 else if(opt==2){ x=read(); 68 if(s.size()){ 69 it=s.lower_bound(dfn[x]); 70 if(it==s.end()) it=s.begin(); t=*L(it); 71 ans+=get(x,p[t])+get(x,p[*it])-get(p[t],p[*it]); 72 } s.insert(dfn[x]); 73 } else if(opt==3){ x=read(); 74 if(s.size()>1){ 75 it=s.find(dfn[x]),t=*L(it),it=R(it); 76 ans-=get(x,p[t])+get(x,p[*it])-get(p[t],p[*it]); 77 } s.erase(dfn[x]); 78 } 79 } return Ot(),0; 80 }