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;
}