1. 程式人生 > >藍皮書:異象石 【dfs序+lca】

藍皮書:異象石 【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 }