首页 > 其他分享 >【模板】最小度限制生成树 题解

【模板】最小度限制生成树 题解

时间:2023-11-22 15:03:02浏览次数:35  
标签:bf 连通 min int 题解 最小 read num 模板

其他的题解感觉都好高级,分享一种好想且好实现的方法。

我们可以先把点 \(s\) 和与其相连的边都删除,我们发现剩下的部分变成了一些连通块。

我们不难发现,当要求与 \(s\) 点相连的边的个数为 \(k\) 时,我们的连通块个数显然是 \(k\) 的。

接下来这个问题就转化成了:\(n - 1\) 个点中生成一个 \(k\) 个连通块,要求边权和 + 每个块中到最小的 点到 \(s\) 的边权 最小(注意断句),其实这就相当于我们生成了最小度限制生成树。

接下来的部分,我们假设 \(s\) 和除 \(s\) 外的每个点都有边。

我们不难发现 \(k = n - 1\) 是简单的,答案就是每个点和 \(s\) 的最小边的和。

我们接下来考虑合并两个连通块(单个点也是联通块),将他们变成一个连通块,我们发现将 \(u\) 连通块并入 \(v\) 的代价是 \({\min}(len_{u \rightarrow v}) - {\max}({\min}(len_{s \rightarrow u}),{\min}(len_{s \rightarrow v}))\)。

我们令 \(f_u\) 为 \({\min}(len_{u \rightarrow v})\),\(g_u\) 为 \({\min}(len_{s \rightarrow u})\)。

\(f_u - {\max}(g_u, g_v)\) 可能很难维护,但是,如果我们换一个角度,我们认为把 \(u\) 连通块并入 \(v\) 的代价记作 \(f_u - g_u\)。当我们统计 \(v\) 并入 \(u\) 的时候,另外一种情况也会被统计。

接下来问题就转化成了我们要全局维护一个 \(f_u - g_u\) 的最小值。

我们对于每一个连通块使用一个堆来维护 \(f_u\),合并连通块就直接并查集合并,同时统计 \(g_u\),堆的合并使用启发式合并,然后全局使用一个堆来维护 \(f_u - g_u\),每次合并后我们取出堆顶合并两点后再对全局的堆进行更新即可。

请注意:

更新的时候我们要注意:如果堆首所对应的 \(u, v\) 已经在同一连通块内,我们要一直弹出直到堆首两个点不在同一连通块内。

复杂度:\({\rm O}(m\times \log^2\ m)\)。

当然,我们也可以使用可并堆,复杂度就可以变成 \({\rm O}(m\times {\log}\ m)\)。

如果每一次我们都在可并堆合并的时候启发式的删除所有两个连通块相连的边,复杂度就变成了\({\rm O}(m\times {\log}\ n)\)(但是我懒了,直接用 pbds,就没法做这个操作)。

我们接下来解决不够的边,如果一开始 \(s\) 与一个点没有边相连,我们就连一条极大的边,最后特判一下就行了。

int n, m, num[L], fa[L], _s, k, ww;

ll ans;

int _u[L], _v[L];

__gnu_pbds::priority_queue<pii, greater<pii>, __gnu_pbds::pairing_heap_tag> s[L], S;

int getfa(int w) {
    if(fa[w] == w) return fa[w];
    else return fa[w] = getfa(fa[w]);
}

void merge(int a, int b) {
    S.pop();
    int af = getfa(a), bf = getfa(b);
    if(s[af].size() > s[bf].size()) swap(af, bf);
    fa[af] = bf;
    num[bf] = min(num[bf], num[af]);
    s[bf].join(s[af]);
    while(!s[bf].empty() && getfa(_u[s[bf].top().second]) == getfa(_v[s[bf].top().second])) s[bf].pop();
    if(!s[bf].empty()) S.push({s[bf].top().first - num[bf], s[bf].top().second});
}

signed main() {
    n = read(), m = read(), _s = read(), k = read();
    if(k == 0) return puts("Impossible"), 0;
    rep(i, 1, n) num[i] = P, fa[i] = i;
    rep(i, 1, m) {
        int u = read(), v = read(), w = read();
        if(u == v) continue;
        if(u != _s && v != _s) s[u].push({w, i}), s[v].push({w, i});
        else num[v] = min(num[v], w), num[u] = min(num[u], w);
        _u[i] = u, _v[i] = v;
    }
    rep(i, 1, n) if(i != _s) ans += num[i];
    rep(i, 1, n) if(i != _s) if(num[i] != P) ww ++;
    if(n - 1 == k) {
        if(k > ww || S.empty()) puts("Impossible"); else cout << ans << endl;
        return 0;
    }
    rep(i, 1, n) if(i != _s) if(s[i].size()) {S.push({s[i].top().first - num[i], s[i].top().second});}
    per(_, n - 2, 1) {
        while(!S.empty() && getfa(_u[S.top().second]) == getfa(_v[S.top().second])) S.pop();
        if(!S.empty()) ans += S.top().first; 
        if(_ == k) {
            if(k > ww || S.empty()) puts("Impossible"); else cout << ans << endl;
            return 0;
        }
        merge(_u[S.top().second], _v[S.top().second]);
    } 
    return 0;
}

标签:bf,连通,min,int,题解,最小,read,num,模板
From: https://www.cnblogs.com/Kafuchino/p/17849017.html

相关文章

  • [IOI2015] Teams 题解
    妙妙题。不难发现,我们对于每个\(k\)取出的人都是满足\(a_i\leqk\leqb_i\)的。经典的,我们直接将\((a_i,b_i)\)转化到二维平面上,将它转化成一个二维数点问题。我们对于每一个询问,都使\(k\)有序,从小到大贪心的选择,也就相当于\(x\)轴限制不断向右,\(y\)轴限制不断......
  • zookeeper3.5.5以上8080端口占用问题解决
    zookeeper3.5.5启动默认会把AdminService服务启动,这个服务默认是8080端口,是一个通过jetty启动的管理控制台,一般不会用到,网上的复制粘贴就是来自同一个办法如下:方法一、删除jetty方法二、修改端口。修改方法的方法有两种:在启动脚本中增加-Dzookeeper.admin.serverPort=你的端......
  • P9868 题解
    blog。NOIP2023T1。可以对字符串随意交换,即可以重排每个单词。对于询问\(i\),最优方案显然是将\(\forallj\nei\)的\(w_j\)重排至字典序最大,将\(w_i\)重排至字典序最小。这件事情本质是将\(w_i\)与\(\min\limits_{j\nei}w_j\)比较。在开始时将全部串重排至字典序......
  • 【AGC】鸿蒙应用软件包上传问题解析
    ​【问题背景】近期收到了一些反馈,一些鸿蒙元服务开发者在发布应用市场的过程中,上传.app包时遇到了不同的报错,导致上传失败,下面来看一下这些报错的具体原因,如何正确打包上传。 【问题描述1】HarmonyOS元服务软件包上传后,提示“软件包解析失败,请重新上传”,错误详情(5)​​​【......
  • Walrus 入门教程:如何创建模板以沉淀可复用的团队最佳实践
    模板是Walrus的核心功能之一,模板创建完成后用户可以重复使用,并在使用过程中逐渐沉淀研发和运维团队的最佳实践,进一步简化服务及资源的部署。用户可以使用HCL语言自定义创建模板,也可以一键复用Terraform社区中上万个成熟的Module。在本文中,我们将以阿里云EC2为例,介绍如何......
  • webpack的html模板中插入变量写法
    vue-cli文档中的描述如下Index文件#public/index.html 文件是一个会被 html-webpack-plugin 处理的模板。在构建过程中,资源链接会被自动注入。另外,VueCLI也会自动注入resourcehint(preload/prefetch、manifest和图标链接(当用到PWA插件时)以及构建过程中处理的Ja......
  • 【模板】链式前向星
    /*https://blog.csdn.net/sugarbliss/article/details/86495945?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522169977002316800215045285%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=169977002316800215045285&biz_id......
  • 【模板】并查集 (洛谷P3367)
    1#include<bits/stdc++.h>2usingnamespacestd;3template<classT>4inlinevoidread(T&s)5{6s=0;7intw=1;8charch=getchar();9while(ch<'0'||ch>'9')10{11......
  • 【模板】线段树1(洛谷P3372)
    1#include<iostream>2#include<cstdio>34usingnamespacestd;56template<classT>7inlinevoidread(T&s)8{9s=0;10intw=1;11charch=getchar();12while(ch<'0�......
  • T401305 平面划分(easy) 题解
    LinkT401305平面划分(easy)Solution平面上\(n\)条直线所划分处的区域最大个数\(L_n\)是多少我们考虑假设已经有\(n-1\)条直线,我们需要画一条直线,这条直线最多和\(n-1\)条直线相交产生\(n\)个新的区域所以我们得到了\[\begin{align*} &L_0=1\\ &L_n=L_{n-1}......