2017.10.19有一次膜你模擬賽,最後一道題是貨車運輸。
dx在比賽前兩分鐘剛A了這道題。。。我就GG了。。。
我只記得是最大生成樹+LCA。於是就碼,結果最大生成樹寫炸了,克魯斯卡爾調了1h才調出來。。。一直調到最後15min才調出來,然後畫了上面那幅圖。當時所有人都在嘲諷我說:你連最大生成樹都寫炸你別想A了。然而測評後,只有我和dxA了這道題233。
於是今天抽空寫篇博客來記錄一下最大生成樹。
其實重點還是樹剖。
先說最大生成樹。
把邊sort一遍後亂跑並查集就好了
但各種細節要維護好啊
inline int fnd(int x){ return x == f[x] ? x : f[x] = fnd(f[x]); }
嗯反正很短背下來好了。。。if(y == n - 1) break;
n是點數m是邊數!一開始手賤打成m了,我說最大生成樹怎麼建的亂七八糟的for(register int i = 0; i <= n; i++) f[i] = i;
一定要賦初值啊!!!f[f1] = f2;
查完了要並啊!!!y++;
並完了邊數要++啊,不然建不出樹啊if(f[i] == i)
如果自己是自己的father那它就是根了,記得建虛根for(register int i = 0; i <= n; i++) hs[i] = 10419
因爲虛根是0,所以重兒子初值爲0一定會出錯的,要附一個大一點的值
總之歷盡千辛萬苦,最大生成樹被我寫出來了。
下面寫樹剖
樹剖大法好!!!
以前不會樹剖,總覺得是一個很高大上的東西,後來發現就是一個巧妙的暴力,通過兩遍dfs,得到一個dfs序,這樣樹上操作就變成了區間操作,隨便維護一下區間就好了。但這個dfs序有很多神奇的性質,能令鏈上操作的複雜度降至logN。
樹剖的核心就是剖
把樹剖成鏈後,每一個節點都有一個top值,記錄它所在的鏈的頂端是誰
這樣如果兩個點的top值一樣,那他們就在同一條鏈上,自然深度淺的節點就是他們的LCA。
如果兩個點不在同一條鏈上,即top值不同時,我們令top深的點往上跳,跳來跳去兩個點就在同一條鏈上了。
那回到這道題,我們要找兩個點間路徑的最小值。dx巧妙的把點權定義爲它到它父親的邊權,根的點權爲-1.這樣求邊權就成了求點權。而我沒有想到如此巧妙的做法,我暴力維護了邊權。我用一個cf數組記錄這個點到它父親的邊權,一個ct數組記錄它到top的最小邊權,dfs時把這兩個數組預處理出來,然後求LCA時就可以用ct數組直接查詢了。但兩個點的top要是一樣了,直接用ct查詢顯然是錯誤的,我就只能讓深的點往上跳,一邊跳一邊用cf來查詢。(如果樹退化成鏈並且每次都查詢鏈底到鏈頂的話就是N2算法了。。。還好數據沒卡)
最後是代碼
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
inline long long read()
{
long long ans = 0, k = 1;
char ch = getchar();
while((ch < '0' || ch > '9') && ch != '-') ch = getchar();
if(ch == '-') k = -1, ch = getchar();
while(ch <= '9' && ch >= '0')
ans = ans * 10 + ch - '0', ch = getchar();
return ans * k;
}
inline void print(long long x)
{
if(x < 0) { putchar('-'); print(-x); return ; }
if(x > 9) print(x / 10);
putchar(x % 10 + '0');
return ;
}
int n, m, p;
struct edge
{
int f, t, w;
};
vector <edge> e;
vector <edge> u[10420];
int f[10420];
int op[10420], sz[10420], hs[10420], dep[10420], top[10420], fa[10420], dfsx_p;
int cf[10420], ct[10420]; //costfather, costtop
inline int fnd(int x) { return x == f[x] ? x : fnd(f[x]); }
inline bool cmp(edge a, edge b) { return a.w > b.w; }
inline void dfs(int x)
{
int len = u[x].size();
sz[x]++;
for(register int i = 0; i < len; i++)
if(u[x][i].t != fa[x])
{
fa[u[x][i].t] = x;
dep[u[x][i].t] = dep[x] + 1;
dfs(u[x][i].t);
sz[x] += sz[u[x][i].t];
cf[u[x][i].t] = u[x][i].w;
if(sz[u[x][i].t] > sz[hs[x]])
hs[x] = u[x][i].t;
}
return ;
}
inline void DFS(int x, int r)
{
if(x == r) ct[x] = 0x7fffffff;
else ct[x] = min(cf[x], ct[fa[x]]);
top[x] = r;
if(hs[x] != 10419) DFS(hs[x], r);
int len = u[x].size();
for(register int i = 0; i < len; i++)
if(u[x][i].t != fa[x] && u[x][i].t != hs[x])
DFS(u[x][i].t, u[x][i].t);
return ;
}
inline void LCA(int x, int y)
{
int ans = 0x7fffffff;
while(top[x] != top[y])
{
if(dep[top[x]] > dep[top[y]]) swap(x, y);
ans = min(ans, min(ct[y], cf[top[y]]));
y = fa[top[y]];
}
if(dep[x] > dep[y]) swap(x, y);
if(x == 0) {printf("-1\n"); return ; }
while(dep[y] > dep[x])
{
ans = min(ans, cf[y]);
y = fa[y];
}
printf("%d\n", ans);
return ;
}
int main()
{
freopen("truck.in", "r", stdin);
freopen("truck.out", "w", stdout);
n = read();
m = read();
register int x, y = 0;
register edge s;
for(register int i = 0; i <= n; i++)
f[i] = i, hs[i] = 10419;
for(register int i = 1; i <= m; i++)
{
s.f = read();
s.t = read();
s.w = read();
e.push_back(s);
}
x = e.size();
sort(e.begin(), e.end(), cmp);
for(register int i = 0; i < x; i++)
{
int f1 = fnd(e[i].f), f2 = fnd(e[i].t);
if(f1 != f2)
{
u[e[i].f].push_back(e[i]);
swap(e[i].f, e[i].t);
u[e[i].f].push_back(e[i]);
y++;
f[f1] = f2;
}
if(y == n - 1) break;
}
for(register int i = 1; i <= n; i++)
if(f[i] == i)
{
s.f = 0;
s.t = i;
s.w = 0x7fffffff;
u[0].push_back(s);
}
dfs(0);
DFS(0, 0);
p = read();
for(register int i = 1; i <= p; i++)
{
x = read();
y = read();
LCA(x, y);
}
return 0;
}