[BZOJ3732]Network

[BZOJ3732]Network

试题描述

给你N个点的无向图 (1 <= N <= 15,000),记为:1…N。 
图中有M条边 (1 <= M <= 30,000) ,第j条边的长度为: d_j ( 1 < = d_j < = 1,000,000,000).

现在有 K个询问 (1 < = K < = 15,000)。 
每个询问的格式是:A B,表示询问从A点走到B点的所有路径中,最长的边最小值是多少?

输入

第一行: N, M, K。 
第2..M+1行: 三个正整数:X, Y, and D (1 <= X <=N; 1 <= Y <= N). 表示X与Y之间有一条长度为D的边。 
第M+2..M+K+1行: 每行两个整数A B,表示询问从A点走到B点的所有路径中,最长的边最小值是多少?

输出

对每个询问,输出最长的边最小值是多少。

输入示例

6 6 8
1 2 5
2 3 4
3 4 3
1 4 8
2 5 7
4 6 2
1 2
1 3
1 4
2 3
2 4
5 1
6 2
6 1

输出示例

5
5
5
4
4
7
4
5

数据规模及约定

1 <= N <= 15,000
1 <= M <= 30,000
1 <= d_j <= 1,000,000,000
1 <= K <= 15,000

题解

一开始我自己yy了一个做法。考虑只有一个询问,那么显然是二分+并查集,然后我就想了一个能够“批处理”多组询问的方法(离线),就是对于答案区间 [al, ar],满足这个答案区间的操作区间为 [ql, qr],那么我们可以像二分那样搞一个 mid = (al + ar) / 2,然后对于 [ql, qr] 区间内的所有询问check一下,像归并排序那样把能够满足答案 mid 的询问放到左边,它们属于新的答案区间 [al, mid],不满足 mid 的询问放到右边,它们属于答案区间 [mid + 1, ar],然后递归处理就好了,边界是当 al = ar 时,所有在这个答案区间中的询问的答案显然都是 al (或 ar),当 ql > qr(即询问集合为空时)直接 return。后来发现这就是传说中的整体二分,看到自己能凭空yy出一个整体二分的方法感到很高兴,然而交上去便T了。。。搞下数据本地开 O2 测是快的飞起啊。。。。。

没办法上网搜一下题解,发现这TM不是 NOIP 题吗?!求一遍最小生成树再搞一个倍增 LCA 就好了。。。。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <cstring>
#include <string>
#include <map>
#include <set>
using namespace std;
 
const int BufferSize = 1 << 16;
char buffer[BufferSize], *Head, *Tail;
inline char Getchar() {
    if(Head == Tail) {
        int l = fread(buffer, 1, BufferSize, stdin);
        Tail = (Head = buffer) + l;
    }
    return *Head++;
}
int read() {
    int x = 0, f = 1; char c = Getchar();
    while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); }
    while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); }
    return x * f;
}
 
#define maxn 15010
#define maxm 30010
#define maxq 20010
#define maxlog 15
int n, m, q;
struct Edge {
    int a, b, c;
    bool operator < (const Edge& t) const { return c < t.c; }
} es[maxm];
 
int pa[maxn];
int findset(int x) { return x == pa[x] ? x : pa[x] = findset(pa[x]); }
 
int ToT, head[maxn], next[maxm], to[maxm], dist[maxm];
void AddEdge(int a, int b, int c) {
    to[++ToT] = b; dist[ToT] = c; next[ToT] = head[a]; head[a] = ToT;
    swap(a, b);
    to[++ToT] = b; dist[ToT] = c; next[ToT] = head[a]; head[a] = ToT;
    return ;
}
 
int dep[maxn], fa[maxn][maxlog], mxd[maxn][maxlog];
void build(int u) {
    for(int i = 1; i < maxlog; i++) {
        int a = fa[u][i-1];
        fa[u][i] = fa[a][i-1];
        mxd[u][i] = max(mxd[u][i-1], mxd[a][i-1]);
    }
    for(int e = head[u]; e; e = next[e]) if(to[e] != fa[u][0]) {
        fa[to[e]][0] = u; mxd[to[e]][0] = dist[e];
        dep[to[e]] = dep[u] + 1;
        build(to[e]);
    }
    return ;
}
int query(int a, int b) {
    if(dep[a] < dep[b]) swap(a, b);
    int ans = 0;
    for(int i = maxlog - 1; i >= 0; i--) if(dep[a] - (1 << i) >= dep[b])
        ans = max(ans, mxd[a][i]), a = fa[a][i];
    if(a == b) return ans;
    for(int i = maxlog - 1; i >= 0; i--) if(fa[a][i] != fa[b][i])
        ans = max(ans, max(mxd[a][i], mxd[b][i])), a = fa[a][i], b = fa[b][i];
    return max(ans, max(mxd[a][0], mxd[b][0]));
}
 
int main() {
    n = read(); m = read(); q = read();
    for(int i = 1; i <= m; i++) es[i].a = read(), es[i].b = read(), es[i].c = read();
     
    sort(es + 1, es + m + 1);
    for(int i = 1; i <= n; i++) pa[i] = i;
    for(int i = 1; i <= m; i++) {
        int u = findset(es[i].a), v = findset(es[i].b);
        if(u != v) {
            pa[v] = u;
            AddEdge(es[i].a, es[i].b, es[i].c);
        }
    }
    build(1);
    while(q--) {
        int a = read(), b = read();
        printf("%d\n", query(a, b));
    }
     
    return 0;
}

 

posted @ 2016-08-10 10:14  xjr01  阅读(180)  评论(0编辑  收藏  举报