zoukankan      html  css  js  c++  java
  • 洛谷P4185 [USACO18JAN]MooTube G 题解

    题意简述

    给定一颗带边权无根树,定义两个点之间的关联度是两点间最短路径的边权最小值

    多次询问,每次询问 (v)(k),求有多少个点满足与 (v) 的关联度不小于 (k)

    解题报告

    首先想一个不是很暴力的暴力

    对于某一个 (k),因为关联度是边权最小值,所以只加入合法的边(边权 (geq k))建一个不一定联通的图,就能删掉不满足的点对。此时图中任意两个联通的点之间都满足关联度 (geq k),统计 (v) 所在连通块大小就能求出答案。

    每次要扫一遍所有的边,维护连通性可以用并查集,时间复杂度 (O(qnlog n))


    发现加边这个过程其实不需要每次都扫一遍

    其实随着 (k) 的逐渐上升,以前加过的边还是会被加,只是会多一些新的边

    于是可以将询问离线下来按 (k) 排序,把边按边权排序,然后处理询问的时候每次只加需要的边

    时间复杂度 (O(n + qlog n))

    代码实现

    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <string>
    #include <vector>
    
    #define forall(G,i) for (int i = 0, __for_siz__ = (int) (G).size(); i < __for_siz__; ++i) 
    #define DEBUG(x) std::cerr << #x << " = " << x << std::endl;
    #define ALL(x) (x).begin(), (x).end()
    #define MP std::make_pair
    #define se second
    #define fi first
    
    using std::cin;
    using std::cout;
    using std::endl;
    
    inline int read() {
        int s = 0, x = 1; char ch = getchar();
        while (!isdigit(ch)) { if (ch == '-') x = -x; ch = getchar(); }
        while (isdigit(ch)) { s = s * 10 + ch - '0'; ch = getchar(); }
        return s * x;
    }
    
    const int MAXN = 100000 + 10;
    
    // 对于一个 k,某点周围所有满足的点都要有 w <= k
    // 因此使用并查集维护连通性,对于每个询问分别加入所有 w <= k 的边
    // 然后发现 k 在单调增的时候可以动态更新边集
    
    int n, q;
    
    struct REdge {
        int u, v, w;
    
        REdge(int _u = 0, int _v = 0, int _w = 0) :
            u(_u), v(_v), w(_w) {}
    } es[MAXN], qry[MAXN];
    
    bool cmp1(REdge r1, REdge r2) {
        return r1.w > r2.w;
    }
    bool cmp2(REdge r1, REdge r2) {
        return r1.w < r2.w;
    }
    
    struct DSU {
        int u[MAXN]; int siz[MAXN];
    
        void Init(int n) {
            for (int i = 1; i <= n; ++i) siz[i] = 1;
        }
        int Find(int x) { return !u[x] ? x : u[x] = Find(u[x]); }
        int Size(int x) { return siz[Find(x)];}
        bool Merge(int x, int y) {
            x = Find(x); y = Find(y);
            if (x == y) return false;
            u[x] = y; siz[y] += siz[x];
            siz[x] = 0; return true;
        }
    } U;
    
    int anss[MAXN];
    
    int main() {
        n = read(); q = read();
        U.Init(n);
        for (int i = 1; i <= n - 1; ++i) {
            int u = read(); int v = read(); int w = read();
            es[i] = REdge(u, v, w);
        }
        for (int i = 1; i <= q; ++i) {
            int k = read(); int u = read();
            qry[i] = REdge(u, i, k);
        } std::sort(es + 1, es + 1 + n - 1, cmp1);
        std::sort(qry + 1, qry + 1 + q, cmp1);
    
        int ii = 1;
        
        for (int iq = 1; iq <= q; ++iq) {
            int k = qry[iq].w; 
            for (; ii <= n - 1; ++ii) {
                if (es[ii].w >= k) {
                    U.Merge(es[ii].u, es[ii].v);
                } else break;
            }
            anss[qry[iq].v] = U.Size(qry[iq].u);
        }
        for (int i = 1; i <= q; ++i) printf("%d
    ", anss[i] - 1);
        return 0;
    }
    
  • 相关阅读:
    pandas
    简单的图片滑动&标签页的前进后退
    xpath 语法&元素交互操作&选项卡操作
    Selenium请求库-day5下午
    初始python
    异步多线程下载网页爬取的视频
    python学习-day4上午
    爬虫初试
    爬虫
    内置模块--又称为常用模块
  • 原文地址:https://www.cnblogs.com/handwer/p/14709497.html
Copyright © 2011-2022 走看看