您的位置:首页 > 其它

[NOIp2013] 货车运输

2016-03-21 21:10 357 查看
题目大意:给一个n(0 < n < 10000)个点,m(0 < m < 50000)条边的带权双向图,给q(0 < q < 30000)个询问,每次询问两个点x, y,输出x到y所有路径上最小边的最大值。

分析:

因为要求所有路径上最小边的最大值,所以先求一遍最大生成树,通过最大生成树的边求得的路径一定是最大的,求的过程中用邻接表建图,之后对于每个询问x, y,用st-lca求x, y的最近公共祖先(LCA),求的过程中对经过的路程取min.

那么,这里介绍一下st-lca算法:

用dep[i]表示结点i的深度,f[i][j]表示结点i的2^j祖先,minv[i][j]表示结点i到其2^j祖先路径上的最小值。

第一步,dfs,得到所有点的dep值,初始化f和minv.

第二步,得到所有的f和minv,其中f[i][j] = f[f[i][j-1]][j-1],minv[i][j] = min(minv[i][j-1], minv[f[i][j-1]][j-1]).

第三步,开始正式lca过程:

1.将y调整为深度比较大的。

2.将询问的两个点调整至相同深度。

3.将两个点调整至lca的儿子。

4.最后返回答案的时候,如果lca是0,那么表示无解,否则最后答案要注意ans = min(ans, min(minv[x][0], minv[y][0])).

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

int n, m, q, x, y, cnt, hd[10005], nxt[20005], to[20005], w[20005], p[50005], f[10005][20], minv[10005][20], dep[10005];
struct edge {
int x, y, z;
bool operator < (const edge &rhs) const {
return z > rhs.z;
}
}e[50005];

void add(int x, int y, int z) {
to[cnt] = y;
w[cnt] = z;
nxt[cnt] = hd[x];
hd[x] = cnt++;
}

int fnd(int x) {
return p[x] == x ? x : p[x] = fnd(p[x]);
}
void kruskal() {
for(int i = 0; i < m; i++) p[i] = i;
sort(e, e+m);
for(int i = 0; i < m; i++) if(fnd(e[i].x) != fnd(e[i].y)) {
add(e[i].x, e[i].y, e[i].z);
add(e[i].y, e[i].x, e[i].z);
p[fnd(e[i].x)] = fnd(e[i].y);
}
}

void dfs(int x, int p) {
for(int i = hd[x]; ~i; i = nxt[i]) if(i != p) {
dep[to[i]] = dep[x] + 1;
f[to[i]][0] = x;
minv[to[i]][0] = w[i];
dfs(to[i], i ^ 1);
}
}

int lca(int x, int y) {
int ans = 100000000;
if(dep[x] > dep[y]) swap(x, y);
for(int i = 15; i >= 0; i--) if(dep[f[y][i]] >= dep[x]) {
ans = min(ans, minv[y][i]);
y = f[y][i];
}
if(x == y) return ans;
for(int i = 15; i >= 0; i--) if(f[x][i] != f[y][i]) {
ans = min(ans, min(minv[x][i], minv[y][i]));
x = f[x][i];
y = f[y][i];
}
return f[x][0] == 0 ? -1 : min(ans, min(minv[x][0], minv[y][0]));
}

int main() {
scanf("%d%d", &n, &m);
for(int i = 0; i < m; i++) scanf("%d%d%d", &e[i].x, &e[i].y, &e[i].z);
memset(hd, -1, sizeof hd);
kruskal();
dep[1] = 1;
dfs(1, -1);
for(int j = 1; j <= 15; j++)
for(int i = 1; i <= n; i++) {
f[i][j] = f[f[i][j-1]][j-1];
minv[i][j] = min(minv[i][j-1], minv[f[i][j-1]][j-1]);
}
scanf("%d", &q);
while(q--) scanf("%d%d", &x, &y), printf("%d\n", lca(x, y));
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: