HDU 3938 Portal(并查集,离线)

题目链接

题目大意

  查询有多少对(x,y),使得x到y至少存在一条路径,路径上的边权值最大值不超过L。

解题思路

  从小到达依次枚举各个边,就能得到若干个图,图里的每条边都不大于当前的最大边(废话)。但是问题在于如何求出每次新加入一条边之后的点的对数,因为所有的边不一定是全都连接一起的。
  如果一条边把一个点连入一个图里的话,那么这个点与图里的每一个点都能形成一个新的点对,那么新增加的点对就是图中的点的个数。如果一条边把一个图与另外一个图相连呢?那么新增的点对就是两个图的点的个数之积。所以我们只要需要用一个并查集来维护所有点之间的关系和每个集合里的点的个数就行了。

代码

const int maxn = 1e4+10;
struct Q {
    int qs, num;
} q[maxn];
struct E {
    int u, v, w;
} e[5*maxn];
int n, m, qes, p[maxn], cnt[maxn], ans[maxn];
int find(int x) {
    return p[x] == x ? p[x] : p[x] = find(p[x]);
}
int main() {
    while(~scanf("%d%d%d",&n,&m,&qes)) {
        for (int i = 0; i<m; ++i) scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
        for (int i = 0; i<qes; ++i) {
            scanf("%d", &q[i].qs);
            q[i].num = i; ans[i] = 0;
        }
        sort(e, e+m, [](E a, E b) {return a.w<b.w;});
        sort(q, q+qes, [](Q a, Q b) {return a.qs<b.qs;});
        for (int i = 1; i<=n; ++i) {
            p[i] = i; cnt[i] = 1;
        }
        int k = 0, sum = 0;
        for (int i = 0; i<m; ++i) {
            while(k<qes&&q[k].qs<e[i].w) ans[q[k++].num] = sum;
            int fa = find(e[i].u), fb = find(e[i].v);
            if (fa!=fb) {
                sum += cnt[fa]*cnt[fb];
                cnt[fb] += cnt[fa];
                p[fa] = fb;
            }
        }
        while(k<qes&&q[k].qs>=e[m-1].w) ans[q[k++].num] = sum;
        for (int i = 0; i<qes; ++i) printf("%d\n", ans[i]);
    }
    return 0;
}
posted @ 2020-05-29 22:34  shuitiangong  阅读(143)  评论(0编辑  收藏  举报