【bzoj2238】Mst(树链剖分+线段树)
2238: Mst
Time Limit:?20 Sec??Memory Limit:?256 MBSubmit:?465??Solved:?131
[Submit][Status][Discuss]
Description
給出一個(gè)N個(gè)點(diǎn)M條邊的無向帶權(quán)圖,以及Q個(gè)詢問,每次詢問在圖中刪掉一條邊后圖的最小生成樹。(各詢問間獨(dú)立,每次詢問不對(duì)之后的詢問產(chǎn)生影響,即被刪掉的邊在下一條詢問中依然存在)Input
第一行兩個(gè)正整數(shù)N,M$(N<=50000,M<=100000)$表示原圖的頂點(diǎn)數(shù)和邊數(shù)。 下面M行,每行三個(gè)整數(shù)X,Y,W描述了圖的一條邊$(X,Y)$,其邊權(quán)為W$(W<=10000)$。保證兩點(diǎn)之間至多只有一條邊。 接著一行一個(gè)正整數(shù)Q,表示詢問數(shù)。$(1<=Q<=100000)$ 下面Q行,每行一個(gè)詢問,詢問中包含一個(gè)正整數(shù)T,表示把編號(hào)為T的邊刪掉(邊從1到M按輸入順序編號(hào))。Output
Q行,對(duì)于每個(gè)詢問輸出對(duì)應(yīng)最小生成樹的邊權(quán)和的值,如果圖不連通則輸出“Not connected”。Sample Input
4 41 2 3
1 3 5
2 3 9
2 4 1
4
1
2
3
4
Sample Output
1513
9
Not connected
數(shù)據(jù)規(guī)模
10%的數(shù)據(jù)$N,M,Q<=100$。另外30%的數(shù)據(jù),$N<=1000$。
100%的數(shù)據(jù)如題目。
第一眼看題以為是求最小生成樹再加一條最小邊,組成最小與次小生成樹的合并(即n個(gè)點(diǎn)n條邊),然后刪的邊如果在生成樹里,就輸出生成樹的邊權(quán)和減去這條邊,如果不在生成樹里,就輸出最小生成樹的邊權(quán)和。 然而這個(gè)做法很快被 パス 掉了(bzoj的題顯然沒這么簡(jiǎn)單),因?yàn)閯h去一條樹邊仍然可能使原圖不連通。因此,這題不是讓求次小生成樹。 經(jīng)過dalao指導(dǎo),發(fā)現(xiàn)這是數(shù)據(jù)結(jié)構(gòu)板子題。 盡管這題不是讓求次小生成樹,但我們依然可以借鑒其某些思路。 我們先做好圖的最小生成樹。 如果刪除的邊不在最小生成樹上,那答案沒變,直接輸出即可。
所以下文討論的 均是刪除的邊在最小生成樹上的情況。 此時(shí)要找能夠代替 刪去的邊 的一條權(quán)值最小的邊。用這條邊代替刪去的邊后就是新的最小生成樹了。 那怎么代替呢? 我們把最小生成樹的樣子畫個(gè)圖看看 我們?nèi)菀装l(fā)現(xiàn)它作為一棵樹的性質(zhì):刪去任意一條邊只會(huì)把樹分成兩個(gè)連通塊。我們要找的是所有 能使兩個(gè)連通塊連通的 非樹邊。這樣的非樹邊就能夠代替刪去的邊重新使樹連通。 那怎么維護(hù) 能夠代替刪去的邊的一條權(quán)值最小的邊?(本題只要求最小生成樹的權(quán)值和,因此可以只維護(hù)所需邊的權(quán)值) 隨便舉個(gè)栗子: 紅叉是刪除的邊,橙/綠線是非最小生成樹的原圖邊。 很容易發(fā)現(xiàn),這些橙邊都可以成為代替邊,而綠邊都不可以成為代替邊。直觀原因是橙邊都連接了兩個(gè)連通塊,而綠邊都沒有連接。 那反過來想,一條非樹邊可以代替哪些邊? 找找簡(jiǎn)單情況(下面的圖中黑邊都是樹邊): 很明顯,這條橙邊可以代替兩條黑邊中的任意一條。 我們往左邊拓展一點(diǎn) 也很明顯,橙邊只能代替右邊兩條黑邊而不能代替最左邊的黑邊,綠邊只能代替最左邊的黑邊而不能代替右邊兩條黑邊。
這時(shí)結(jié)論就出來了:一條非樹邊可以代替其兩端點(diǎn)在樹上的簡(jiǎn)單路徑之間的所有邊
證明:
?
以這個(gè)圖為例,一條非樹邊與其兩端點(diǎn)在樹上的簡(jiǎn)單路徑組成的是一個(gè)x個(gè)點(diǎn)x條邊的環(huán),而這x個(gè)點(diǎn)可以刪去其中一條邊,只用x-1條邊連通,因此這條非樹邊可以代替簡(jiǎn)單路徑中的任意一條樹邊。
相反,這條非樹邊與 不在上述簡(jiǎn)單路徑中的樹邊 只能形成x個(gè)點(diǎn)x-1條邊的樹,刪去任意一條邊都會(huì)使這棵樹不連通,而這棵樹作為原最小生成樹的一棵子樹,這棵子樹不連通的話原最小生成樹一定也不連通。所以這條非樹邊不能代替任意一條非簡(jiǎn)單路徑上的邊。
?
如何快速處理樹上的簡(jiǎn)單路徑?樹鏈剖分!
所以算法明確了:先預(yù)處理出最小生成樹,然后將這棵最小生成樹進(jìn)行樹鏈剖分,每一條邊記錄能夠代替它的、權(quán)值最小的非樹邊的長(zhǎng)度。對(duì)于每條非樹邊,利用樹剖快速更新其兩端點(diǎn)在樹上的簡(jiǎn)單路徑之間的所有邊記錄的最小值。查詢時(shí)直接查詢刪除的邊上存儲(chǔ)的最小值即可。
實(shí)際代碼可以將邊的信息存在其兒子節(jié)點(diǎn)上,簡(jiǎn)化代碼復(fù)雜度
有兩種情況要特判Not connected:
? ?原圖不連通,對(duì)于所有詢問都輸出Not connected(樹鏈剖分只是維護(hù)用其它邊替換一條邊的情況,如果原圖不連通的話,不但生成樹會(huì)建錯(cuò),而且替換了邊樹也不連通);
? ?原圖連通,但刪去這條邊后沒有邊能替它連通兩個(gè)塊,此時(shí)樹鏈剖分上詢問這條邊所得到的值應(yīng)該是沒更新過的初值,即inf。因此判斷這個(gè)詢問值如果是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序號(hào)對(duì)應(yīng)的節(jié)點(diǎn)編號(hào) 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錯(cuò)誤 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){ //由于查詢的是一條被斷掉的邊的兩端點(diǎn),因此這兩點(diǎn)在原樹上是相連的,只有當(dāng)這條邊是輕邊的時(shí)候才會(huì)做這個(gè) 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; //預(yù)處理并查集 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?
轉(zhuǎn)載于:https://www.cnblogs.com/scx2015noip-as-php/p/bzoj2238.html
總結(jié)
以上是生活随笔為你收集整理的【bzoj2238】Mst(树链剖分+线段树)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Integer’s Power HDU
- 下一篇: EMbedding