【bzoj2238】Mst(樹鏈剖分+線段樹)


2238: Mst

Time Limit: 20 Sec  Memory Limit: 256 MB
Submit: 465  Solved: 131
[Submit][Status][Discuss]

Description

給出一個N個點M條邊的無向帶權圖,以及Q個詢問,每次詢問在圖中刪掉一條邊后圖的最小生成樹。(各詢問間獨立,每次詢問不對之后的詢問產生影響,即被刪掉的邊在下一條詢問中依然存在)

Input

第一行兩個正整數N,M$(N<=50000,M<=100000)$表示原圖的頂點數和邊數。
下面M行,每行三個整數X,Y,W描述了圖的一條邊$(X,Y)$,其邊權為W$(W<=10000)$。保證兩點之間至多只有一條邊。
接着一行一個正整數Q,表示詢問數。$(1<=Q<=100000)$
下面Q行,每行一個詢問,詢問中包含一個正整數T,表示把編號為T的邊刪掉(邊從1到M按輸入順序編號)。

Output

Q行,對於每個詢問輸出對應最小生成樹的邊權和的值,如果圖不連通則輸出“Not connected”。

Sample Input

4 4
1 2 3
1 3 5
2 3 9
2 4 1
4
1
2
3
4

Sample Output

15
13
9
Not connected

數據規模

10%的數據$N,M,Q<=100$。
另外30%的數據,$N<=1000$。
100%的數據如題目。

 
第一眼看題以為是求最小生成樹再加一條最小邊,組成最小與次小生成樹的合並(即n個點n條邊),然后刪的邊如果在生成樹里,就輸出生成樹的邊權和減去這條邊,如果不在生成樹里,就輸出最小生成樹的邊權和。
然而這個做法很快被 パス 掉了(bzoj的題顯然沒這么簡單),因為刪去一條樹邊仍然可能使原圖不連通。因此,這題不是讓求次小生成樹。
經過dalao指導,發現這是數據結構板子題。
盡管這題不是讓求次小生成樹,但我們依然可以借鑒其某些思路。
我們先做好圖的最小生成樹。
如果刪除的邊不在最小生成樹上,那答案沒變,直接輸出即可。
所以下文討論的 均是刪除的邊在最小生成樹上的情況。
此時要找能夠代替 刪去的邊 的一條權值最小的邊。用這條邊代替刪去的邊后就是新的最小生成樹了。
那怎么代替呢?
我們把最小生成樹的樣子畫個圖看看
我們容易發現它作為一棵樹的性質:刪去任意一條邊只會把樹分成兩個連通塊。我們要找的是所有 能使兩個連通塊連通的 非樹邊。這樣的非樹邊就能夠代替刪去的邊重新使樹連通。
那怎么維護 能夠代替刪去的邊的一條權值最小的邊?(本題只要求最小生成樹的權值和,因此可以只維護所需邊的權值)
隨便舉個栗子:
紅叉是刪除的邊,橙/綠線是非最小生成樹的原圖邊。
很容易發現,這些橙邊都可以成為代替邊,而綠邊都不可以成為代替邊。直觀原因是橙邊都連接了兩個連通塊,而綠邊都沒有連接。
那反過來想,一條非樹邊可以代替哪些邊?
找找簡單情況(下面的圖中黑邊都是樹邊):
很明顯,這條橙邊可以代替兩條黑邊中的任意一條。
 
我們往左邊拓展一點
 
也很明顯,橙邊只能代替右邊兩條黑邊而不能代替最左邊的黑邊,綠邊只能代替最左邊的黑邊而不能代替右邊兩條黑邊。

這時結論就出來了:一條非樹邊可以代替其兩端點在樹上的簡單路徑之間的所有邊

證明:

 

以這個圖為例,一條非樹邊與其兩端點在樹上的簡單路徑組成的是一個x個點x條邊的環,而這x個點可以刪去其中一條邊,只用x-1條邊連通,因此這條非樹邊可以代替簡單路徑中的任意一條樹邊。

相反,這條非樹邊與 不在上述簡單路徑中的樹邊 只能形成x個點x-1條邊的樹,刪去任意一條邊都會使這棵樹不連通,而這棵樹作為原最小生成樹的一棵子樹,這棵子樹不連通的話原最小生成樹一定也不連通。所以這條非樹邊不能代替任意一條非簡單路徑上的邊。

 

如何快速處理樹上的簡單路徑?樹鏈剖分!

所以算法明確了:先預處理出最小生成樹,然后將這棵最小生成樹進行樹鏈剖分,每一條邊記錄能夠代替它的、權值最小的非樹邊的長度。對於每條非樹邊,利用樹剖快速更新其兩端點在樹上的簡單路徑之間的所有邊記錄的最小值。查詢時直接查詢刪除的邊上存儲的最小值即可。

實際代碼可以將邊的信息存在其兒子節點上,簡化代碼復雜度

有兩種情況要特判Not connected:

   原圖不連通,對於所有詢問都輸出Not connected(樹鏈剖分只是維護用其它邊替換一條邊的情況,如果原圖不連通的話,不但生成樹會建錯,而且替換了邊樹也不連通);

   原圖連通,但刪去這條邊后沒有邊能替它連通兩個塊,此時樹鏈剖分上詢問這條邊所得到的值應該是沒更新過的初值,即inf。因此判斷這個詢問值如果是inf就輸出Not connected即可。

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<iostream>
  4 #include<algorithm>
  5 #define inf 2147483647
  6 #define N 50005
  7 #define M 100005
  8 using namespace std;
  9 inline int read(){
 10     int x=0; bool f=1; char c=getchar();
 11     for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
 12     for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^'0');
 13     if(f) return x;
 14     return 0-x;
 15 }
 16 int n,m,q;
 17 struct inedge{
 18     int id,u,v,w;
 19     bool operator <(const inedge &x)const{
 20         return w<x.w;
 21     }
 22 }in_e[M],pre_e[M];
 23 
 24 int head[M];
 25 struct edge{
 26     int v,w,next;
 27 }e[N];
 28 inline int add(int i,int u,int v,int w){
 29     e[i].v=v, e[i].w=w, e[i].next=head[u], head[u]=i;
 30 }
 31 
 32 int seg[N<<2],tag[N<<2];
 33 int top[M],id[M],r_id[M],tot; //id是dfs序,r_id是dfs序號對應的節點編號
 34 
 35 void pushdown(int o){
 36     if(tag[o]==inf) return;
 37     seg[o<<1]   = min(seg[o<<1],   tag[o]);
 38     seg[o<<1|1] = min(seg[o<<1|1], tag[o]);
 39     tag[o<<1]   = min(tag[o<<1],   tag[o]);
 40     tag[o<<1|1] = min(tag[o<<1|1], tag[o]);
 41     tag[o]=inf;
 42 }
 43 
 44 void build(int l,int r,int o){
 45     seg[o]=tag[o]=inf;
 46     if(l==r) return;  //in_e[edge_p[r_id[l]]>>1].w 這sb錯誤 
 47     int mid=(l+r)>>1;
 48     build(l,mid,o<<1),
 49     build(mid+1,r,o<<1|1);
 50 }
 51 
 52 void update(int l,int r,int o,int left,int right,int val){
 53     
 54     if(l>=left && r<=right){
 55         seg[o]=min(seg[o],val);
 56         tag[o]=min(tag[o],val);
 57         return;
 58     }
 59     pushdown(o);
 60     int mid=(l+r)>>1;
 61     if(mid>=left) update(l,mid,o<<1,left,right,val);
 62     if(mid<right) update(mid+1,r,o<<1|1,left,right,val);
 63 }
 64 
 65 int query(int l,int r,int o,int left,int right){
 66     if(l>=left && r<=right) return seg[o];
 67     pushdown(o);
 68     int mid=(l+r)>>1, ret=inf;
 69     if(mid>=left) ret = min(ret, query(l,mid,o<<1,left,right));
 70     if(mid<right) ret = min(ret, query(mid+1,r,o<<1|1,left,right));
 71     return ret;
 72 }
 73 
 74 int fa[M],size[M],dep[M],hson[M];
 75 void dfs1(int u,int f){
 76     fa[u]=f, size[u]=1;
 77     for(int i=head[u];i;i=e[i].next){
 78         int v=e[i].v;
 79         if(f!=v){
 80             dep[v]=dep[u]+1;
 81             dfs1(v,u);
 82             if(hson[u]==0 || size[hson[u]]<size[v]) hson[u]=v;
 83             size[u]+=size[v];
 84         }
 85     }
 86 }
 87 
 88 void dfs2(int u,int anc){ //anc表示重鏈鏈頂
 89     top[u]=anc, id[u]=tot, r_id[tot++]=u;
 90     if(hson[u]==0) return;
 91     dfs2(hson[u],anc);
 92     for(int i=head[u];i;i=e[i].next){ 
 93         int v=e[i].v;
 94         if(v!=fa[u] && v!=hson[u]) dfs2(v,v);
 95     }
 96 }
 97 
 98 void chain_update(int u,int v,int w){
 99     int tu=top[u],tv=top[v];
100     while(tu!=tv){
101         if(dep[tu]<dep[tv]) swap(tu,tv), swap(u,v);
102         update(1,n-1,1,id[tu],id[u],w);
103         u=fa[tu];
104         tu=top[u];
105     }
106     if(u==v) return;
107     if(dep[u]<dep[v]) swap(u,v);
108     update(1,n-1,1,id[hson[v]],id[u],w);
109 }
110  
111 int chain_query(int u,int v){
112     int tu=top[u],tv=top[v];
113     if(tu!=tv){ //由於查詢的是一條被斷掉的邊的兩端點,因此這兩點在原樹上是相連的,只有當這條邊是輕邊的時候才會做這個 
114         if(dep[tu]<dep[tv]) swap(tu,tv), swap(u,v);
115         return query(1,n-1,1,id[tu],id[u]);
116     }
117     else{
118         if(dep[u]<dep[v]) swap(u,v);
119         return query(1,n-1,1,id[hson[v]],id[u]);
120     }
121 }
122 
123 int bcj[N],cnt,sum;
124 bool istree[M],pre_istree[M];
125 int find(int x){return x==bcj[x] ? x : bcj[x]=find(bcj[x]);}
126 bool merge(int x,int y){
127     int fx=find(x),fy=find(y);
128     if(fx==fy) return 0; 
129     bcj[fy]=fx;
130     return 1;
131 }
132 int main(){
133     //freopen("faq.in","r",stdin);
134     //freopen("count.out","w",stdout);
135     n=read(),m=read();
136     int i,u,v,w,Q,T,cx;
137     for(i=1;i<=m;i++)
138         in_e[i].id=i, in_e[i].u=pre_e[i].u=read(), in_e[i].v=pre_e[i].v=read(), in_e[i].w=pre_e[i].w=read();
139     sort(in_e+1,in_e+m+1);
140     for(i=1;i<=n;i++) bcj[i]=i; //預處理並查集
141     for(i=1;i<=m;i++){ //最小生成樹建出來,樹鏈剖分用
142         if(merge(in_e[i].u, in_e[i].v)){ //兩塊未連通
143             add(i<<1  , in_e[i].u, in_e[i].v, in_e[i].w),
144             add(i<<1|1, in_e[i].v, in_e[i].u, in_e[i].w);
145             istree[i]=1, pre_istree[in_e[i].id]=1;
146             sum+=in_e[i].w;
147             if(++cnt==n-1) break;
148         }
149     }
150     
151     if(cnt<n-1){ //整張圖不連通 
152         Q=read();
153         for(i=0;i<Q;i++){T=read(); printf("Not connected\n");}
154         return 0;
155     }
156         
157     dep[1]=1;
158     dfs1(1,0); //建樹鏈
159     dfs2(1,1); //分輕重鏈
160     
161     build(1,n-1,1);
162     for(i=1;i<=m;i++)
163         if(!istree[i]) chain_update(in_e[i].u, in_e[i].v, in_e[i].w);
164         
165     Q=read();
166     for(i=0;i<Q;i++){
167         T=read();
168         if(!pre_istree[T]) {printf("%d\n",sum); continue;}
169         int cx=chain_query(pre_e[T].u,pre_e[T].v);
170         if(cx==inf) printf("Not connected\n");
171         else printf("%d\n",sum-pre_e[T].w+cx);
172     }
173     return 0;
174 }
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM