首页 > 其他分享 >2024牛客暑期多校训练营2 B.MST(题解)

2024牛客暑期多校训练营2 B.MST(题解)

时间:2024-07-19 09:43:01浏览次数:14  
标签:arr int 题解 询问 MST 多校 sqrt fa maxn

题意

给一张 \(n\) 个点, \(m\) 条边的无向图,\(q\) 次询问,每次询问给 \(k\) 个结点,问这 \(k\) 个结点的诱导子图(也就是原图中抽出这些结点,以及原图中这些节点之间有的边) 的最小生成树是多少,不连通输出-1, 保证 \(q\) 次询问加起来问到的点的数量 \(\sum k_i \leq 10^5\)。

思路

就1秒时间,这个问题看着很困难,很容易想到要去根号分治,但是队友说,无论怎么分,给你 \(\sqrt n\) 次询问,每次询问 \(\sqrt n\) 个点的完全图,总的边数都是 \(n \sqrt n\) 级别,再用最小生成树的算法,铁t。

然后我赛时的想法是,既然询问到的总边数肯定是 \(n \sqrt n\)级别,那肯定得让最小生成树那边求的快一点,不可能每次都排序用克鲁斯卡尔。然后思路变成,总的对边排一次序,然后遍历每条边,判度这些边需要添加到哪些询问里。然后因此对询问的大小进行根号分治,询问大的,直接用数组存起来它们都问了哪些点。小的,枚举顶点 \(n^2\) 预处理,可能涉及到的边。然后离线同时对这多个询问的并查集去维护。最后还是t了。

赛后看群里,发现大家就是直接在线跑 \(q\) 次克鲁斯卡尔 \(n \sqrt n logn\) 冲过去了,然后每次是对加入的边进行根号分治。如果询问的顶点大于 \(\sqrt n\) 个,直接遍历已经排序过的总的边,判断每个边的两个顶点是否都在这个询问内,在的话加入并查集维护。因为这样的询问不超过 \(\frac{n}{\sqrt n} = \sqrt n\) 个,所以复杂度是 \(\sqrt n \times m\),然后询问的点比较少,那么直接 \(O(n^2)\) 的遍历两个顶点,看看map存的邻接矩阵里面有没有这条边,有的话把这个边加入并查集维护。复杂度是 \(O((\sqrt n) ^ 2 logn)\)。然后块长就设个 \(\sqrt n\) 附近,就能直接冲过去了。(看排行榜前面杭电的代码,块长设了一个奇妙的 \(\frac{m}{log(m)}\)),时间会快很多,不知道是怎么算的。

代码如下:

#include <bits/stdc++.h>
#define ll long long
#define pii pair<int, int>
#define all(a) a.begin(), a.end()
using namespace std;
const int maxn = 1e5 + 10;
const int mod = 998244353;
struct edge {
    int u, v, w;
} e[maxn];
bool operator<(edge a, edge b) {
    return a.w < b.w;
}
map<pii, int> mp;
int fa[maxn], arr[maxn];
int us[maxn];
int Find(int x) {
    return fa[x] == x ? fa[x] : fa[x] = Find(fa[x]);
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    // freopen("./B-MST/6.in", "r", stdin);
    // freopen("1.out", "w", stdout);
    int n, m, q;
    cin >> n >> m >> q;
    int sz = sqrt(m / log2(m));
    for (int i = 1; i <= m; i++) {
        int u, v, w;
        cin >> u >> v >> w;
        e[i] = edge{u, v, w};
        mp[pii(u, v)] = mp[pii(v, u)] = w;
    }
    sort(e + 1, e + 1 + m);
    while (q--) {
        int k;
        cin >> k;
        for (int i = 1; i <= k; i++)
            cin >> arr[i], fa[arr[i]] = arr[i];
        if (k > sz) {
            vector<edge> tem;
            for (int i = 1; i <= m; i++) {
                auto [u, v, w] = e[i];
                if (fa[u] && fa[v])
                    tem.push_back(edge{u, v, w});
            }
            sort(all(tem));
            int cnt = k;
            ll ans = 0;
            for (auto [u, v, w] : tem) {
                u = Find(u), v = Find(v);
                if (u != v) {
                    fa[u] = v;
                    ans += w;
                    cnt--;
                }
            }
            if (cnt == 1)
                cout << ans << "\n";
            else
                cout << -1 << "\n";
        } else {
            vector<edge> tem;
            for (int i = 1; i <= k; i++)
                for (int j = i + 1; j <= k; j++)
                    if (mp.count(pii(arr[i], arr[j])))
                        tem.push_back(
                            edge{arr[i], arr[j], mp[pii(arr[i], arr[j])]});
            sort(all(tem));
            int cnt = k;
            ll ans = 0;
            for (auto [u, v, w] : tem) {
                u = Find(u), v = Find(v);
                if (u == v)
                    continue;
                fa[u] = v;
                ans += w;
                cnt--;
            }
            if (cnt == 1)
                cout << ans << "\n";
            else
                cout << -1 << "\n";
        }
        for (int i = 1; i <= k; i++)
            fa[arr[i]] = 0;
    }
    return 0;
}

标签:arr,int,题解,询问,MST,多校,sqrt,fa,maxn
From: https://www.cnblogs.com/TJUHuangTao/p/18310814

相关文章

  • 题解:CF1381A1 Prefix Flip (Easy Version)
    思路这道题直接用下一题的代码就行了\(C1\):注意到限制\(3n\)很大,于是看每一位是不是一样的,再操作,如样例一:转化第一位:\(01\to11\)。转化第二位:\(11\to00\to10\)。每次把当前位子提到第一位,然后翻转第一位,最后翻转回去,最多\(3n\)次,不用暴力操作直接计答案时间复杂度......
  • 牛客多校H题题解
    链接:[https://ac.nowcoder.com/acm/contest/81597/H]来源:牛客网题目描述Redstandsatthecoordinate\((0,0)\)oftheCartesiancoordinatesystem.Shehasastringofinstructions:up,down,left,right(where'right'increasesthex-coordinateby\(1......
  • 题解
    A-地毯标准的二维差分前缀和,定义\(s_{i,j}\)为当前格子的权值,然后根据题目模拟题意进行差分求和即可#include<bits/stdc++.h>#defineintlonglongusingnamespacestd;constintN=1e3+10,mod=1e9+7;ints[N][N];signedmain(){std::ios::sync_with_......
  • 题解:2024牛客多校赛第二场 A Floor Tiles(思维)
    2024NowcoderMulti-UniversityTrainingContest2ProblemA.FloorTiles题目大意给你两种正方形图案,分别为以下两种:再给你三个整数\(N,M,K\),表示你需要用这两种图案,拼成一个\(N\)列\(M\)行的矩形。由于这两种图案十分特殊,他们能无缝衔接在一起。因此你需要让这个矩......
  • 2024牛客暑期多校训练营1
    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档文章目录A.ABitCommonC.SumofSuffixSumsH.WorldFinalsA.ABitCommon题意:给出n和m两个整数(n,m<=5000),计算符合下列条件的序列A的个数:·序列A长n,每个元素小于2^m·存在某个非空子序......
  • 2024牛客暑期多校训练营2
    Preface最下班的一集,100min的时候手速过了六题,本来以为是完美开场,没想到是坐牢的开始J题很快推出了一个\(O(n)\)计算的组合式子,然后扔给徐神找生成函数做法,中间给出了几个要写快速阶乘算法的假做法后发现这题不太可做祁神开始转战D题后给了个基于纳什均衡的很对的DP做......
  • GESP编程能力等级认证C++编程真题解析 | 2024年3月五级
    学习C++从娃娃抓起!记录下CCF-GESP备考学习过程中的题目,记录每一个瞬间。附上汇总贴:GESP编程能力等级认证C++编程真题解析|汇总单选题第1题唯一分解定理描述的内容是()?A.任意整数都可以分解为素数的乘积B.每个合数都可以唯一分解为一系列素数的乘积C.两个不同的......
  • 题解:CF1912D Divisibility Test
    又是一道水绿。刚刚小学毕业的数学idiot——我释怀地笑了。第一种很好判断,当$b^k$为$n$的倍数时,取基数为$b$的能被$n$整除的整数$c$的最后$k$位数显然能被$n$整除。第二种也不难,当$b^k\equiv1\pmodn$时,取以$b$为底数的能被$n$整除的整数$c$的$k$......
  • 2024牛客暑期多校训练营2
    E题意:给定一个数\(x\),找出严格小于\(x\)的一个数\(y\)使得\(gcd(x,y)=x\oplusy\)。赛时小\(wa\)一次,答案就是\(x-lowbit(x)\)(不为\(0\)的前提下)。C题意:HB(补题)题意:给定图,\(q\)次询问,每次给出一个点集,求解该点集的最小生成树。(保证询问的点数之和不超过\(......
  • Load balancer does not contain an instance for the service service-B [503] duri
    场景:service-A服务通过openFeign远程调用service-B服务的test()方法,结果报错Loadbalancerdoesnotcontainaninstancefortheserviceservice-Bfeign.FeignException$ServiceUnavailable:[503]during[POST]to[http://service-B/test]原因:报错信息的意思......