LINK:CF125E MST Company

难点在于构造 前面说到了求最小值 可以二分出斜率k然后进行\(Kruskal\) 然后可以得到最小值。\(mx\)为值域.

得到最小值之后还有一个构造问题 值得注意的是虽然得到的权值是最小的 也是合法的 但是此时拿到的边不一定合法。

出现这种情况的原因是最小生成树的边的权值相等了 所以白边有限那么久多余了。

实际上可以构造出了的。

考虑如何构造:

第一种是考虑最小k度生成树的做法。先将除1以外的联通块做一下。然后不断加边。

加成一颗树的时候如果不满足k度 那么再次加边 在边形成的这个环中找到除了和1相连的最大边 然后在所有的边中选取影响最小的。

一直重复是的1的度数为k.每次暴力dfs预处理一下. 复杂度\(n\cdot k+mlogm\)

值得一提的是这个做法脱离了Wqs二分 比较暴力 但是是一个比较经典的做法。

第二种是直接替换法。

二分得到ans之后 所有和1相连的边加上ans 此时可能1的度数cnt>k.

考虑利用其他边来替换和1相连的那些边 如果一条边的权值和1相连的某条边权值相同 且分属不同子树中就可以替换。

复杂度\(nlogmx+nlogn+mlogm\)复杂度算是比较优秀 正确性可以确保。

第三种是直接构造法。

还是分析本质原因 边之间的替换问题。

一个比较重要的结论是最小生成树的边的权值个数是一定的。

也就是前轮到某个权值 这种权值数量一定。同时可以得到当进行到某一种权值的时候 树的形态也是一定的。可以考虑利用权值分层处理构造。

设w[x]表示比x大的权值的和1相连的边最多的个数。

那么当前拿和1相连的边的个数就知道了 直接拿就行了 拿够了就拿其他的边 保证了后续是一定是满足的。

复杂度\(mlogm+nlogmx\)

第三种比较繁琐 第一种不够优秀 所以使用的是第二种方法。

code
//#include<bits\stdc++.h>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define ll long long
#define db double
#define INF 1000000000
#define ldb long double
#define pb push_back
#define put_(x) printf("%d ",x);
#define get(x) x=read()
#define gt(x) scanf("%d",&x)
#define gi(x) scanf("%lf",&x)
#define put(x) printf("%d\n",x)
#define putl(x) printf("%lld\n",x)
#define gc(a) scanf("%s",a+1)
#define rep(p,n,i) for(RE int i=p;i<=n;++i)
#define go(x) for(int i=lin[x],tn=ver[i];i;tn=ver[i=nex[i]])
#define fep(n,p,i) for(RE int i=n;i>=p;--i)
#define vep(p,n,i) for(RE int i=p;i<n;++i)
#define pii pair<int,int>
#define mk make_pair
#define RE register
#define P 1000000007
#define gf(x) scanf("%lf",&x)
#define pf(x) ((x)*(x))
#define uint unsigned long long
#define ui unsigned
#define EPS 1e-8
#define sq sqrt
#define S second
#define F first
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
RE int x=0,f=1;RE char ch=getc();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
return x*f;
}
const int MAXN=100010,maxn=5010;
int n,m,k,cnt1,cnt2,cnt,ans,len;
int f[maxn];set<int>s;
int lin[maxn],ver[maxn<<1],nex[maxn<<1],e[maxn<<1],v[maxn],e1[maxn],b[maxn];
inline void add(int x,int y,int z,int z1)
{
ver[++len]=y;nex[len]=lin[x];lin[x]=len;e1[len]=z1;e[len]=z;
ver[++len]=x;nex[len]=lin[y];lin[y]=len;e1[len]=z1;e[len]=z;
s.insert(z1);
}
struct wy
{
int x,y,z,id;
inline bool friend operator <(wy a,wy b){return a.z<b.z;}
}t[maxn],w[maxn],tmp1[MAXN],tmp2[MAXN];
inline int getfather(int x){return x==f[x]?x:f[x]=getfather(f[x]);}
inline bool merge(int x,int y)
{
int xx=getfather(x);
int yy=getfather(y);
if(xx==yy)return 0;
f[xx]=yy;return 1;
}
inline void dfs(int x,int fa)
{
f[x]=fa;
go(x)if(tn!=fa)dfs(tn,x);
}
inline int check(int x)
{
ans=cnt=0;int i=1,j=1;
rep(1,n,i)f[i]=i;
rep(1,cnt1+cnt2,T)
{
if(i<=cnt1&&j<=cnt2)
{
if(t[i].z+x<=w[j].z){if(merge(t[i].x,t[i].y))ans+=t[i].z+x,++cnt;++i;}
else {if(merge(w[j].x,w[j].y))ans+=w[j].z;++j;}
continue;
}
if(i<=cnt1){if(merge(t[i].x,t[i].y))ans+=t[i].z+x,++cnt;++i;}
else {if(merge(w[j].x,w[j].y))ans+=w[j].z;++j;}
}
return cnt>=k;
}
int main()
{
//freopen("1.in","r",stdin);
get(n);get(m);get(k);
rep(1,n,i)f[i]=i;int cc=0;
rep(1,m,i)
{
int get(x),get(y),get(z);
if(x==1||y==1)tmp1[++cnt1]=(wy){x,y,z,i};
else tmp2[++cnt2]=(wy){x,y,z,i};
if(merge(x,y))++cc;
}
if(cc!=n-1){puts("-1");return 0;}
sort(tmp1+1,tmp1+1+cnt1);
sort(tmp2+1,tmp2+1+cnt2);
rep(1,n,i)f[i]=i;cc=0;
rep(1,cnt1,i)if(merge(tmp1[i].x,tmp1[i].y))t[++cc]=tmp1[i];
rep(1,n,i)f[i]=i;cnt1=cc;cc=0;
rep(1,cnt2,i)if(merge(tmp2[i].x,tmp2[i].y))w[++cc]=tmp2[i];
cnt2=cc;int l=-100000,r=100000;
if(!check(l)){puts("-1");return 0;}
check(r);if(cnt>k){puts("-1");return 0;}
while(l+1<r)
{
int mid=(l+r)>>1;
if(check(mid))l=mid;
else r=mid;
}
if(check(r))l=r;
rep(1,cnt1,i)t[i].z+=l;
rep(1,n,i)f[i]=i;ans=cnt=0;int i=1,j=1;
rep(1,cnt1+cnt2,T)
{
if(i<=cnt1&&j<=cnt2)
{
if(t[i].z<=w[j].z){if(merge(t[i].x,t[i].y))ans+=t[i].z,add(t[i].x,t[i].y,t[i].z,t[i].id),++cnt;++i;}
else {if(merge(w[j].x,w[j].y))ans+=w[j].z,add(w[j].x,w[j].y,w[j].z,w[j].id);++j;}
continue;
}
if(i<=cnt1){if(merge(t[i].x,t[i].y))ans+=t[i].z,add(t[i].x,t[i].y,t[i].z,t[i].id),++cnt;++i;}
else {if(merge(w[j].x,w[j].y))ans+=w[j].z,add(w[j].x,w[j].y,w[j].z,w[j].id);++j;}
}
put(n-1);
go(1)
{
dfs(tn,1);
f[tn]=tn;
b[tn]=e1[i];
v[tn]=e[i];
}
for(int i=1;i<=cnt2;++i)
{
if(cnt==k)break;
int xx=getfather(w[i].x);
int yy=getfather(w[i].y);
if(xx==yy)continue;
if(v[xx]==w[i].z)
{
f[xx]=yy;
s.erase(b[xx]);
s.insert(w[i].id);
--cnt;
continue;
}
if(v[yy]==w[i].z)
{
f[yy]=xx;
s.erase(b[yy]);
s.insert(w[i].id);
--cnt;
}
}
for(set<int>::iterator it=s.begin();it!=s.end();++it)printf("%d ",*it);
return 0;
}
</details>

luogu CF125E MST Company wqs二分 构造的更多相关文章

  1. CF125E MST company (凸优化+MST)

    qwq自闭的一个题 我来修锅辣!!!!!! 这篇题解!可以\(hack\)全网大部分的做法!!! 首先,我们可以把原图中的边,分成两类,一类是与\(1\)相连,另一类是不与\(1\)相连. 原题就转化 ...

  2. [CF125E]MST Company

    codeforces description 给出一张\(n\)点\(m\)条边的无向图,求一棵满足\(1\)号点度数恰好为\(k\)的最小生成树,并输出方案. \(1\le k\le n\le500 ...

  3. CF-125E MST Company (单度限制最小生成树)

    参考红宝书 题目链接 对除 1 号点顶点外的点集,求一次最小生成森林,对于最小生成森林的联通分量,选择最短的一条边与 1 号点相连.设此时 1 号点的度为 \(k_0\),如果 \(k_0\lt L\ ...

  4. 【CF125E】MST Company(凸优化,最小生成树)

    [CF125E]MST Company(凸优化,最小生成树) 题面 洛谷 CF 题解 第一眼看见就给人丽洁姐那道\(tree\)一样的感觉. 那么二分一个权值,加给所有有一个端点是\(1\)的边, 然 ...

  5. luogu P5633 最小度限制生成树 wqs二分

    LINK:最小度限制生成树 还是WQS二分的模板题 不过相当于我WQS二分的复习题. 对于求出强制k个的答案 dp能做不过复杂度太高了. 世界上定义F(x)表示选出x个的答案 画成图像 其实形成了一个 ...

  6. Luogu P2619 [国家集训队2]Tree I(WQS二分+最小生成树)

    P2619 [国家集训队2]Tree I 题意 题目描述 给你一个无向带权连通图,每条边是黑色或白色.让你求一棵最小权的恰好有\(need\)条白色边的生成树. 题目保证有解. 输入输出格式 输入格式 ...

  7. 关于WQS二分算法以及其一个细节证明

    应用分析 它的作用就是题目给了一个选物品的限制条件,要求刚好选$m$个,让你最大化(最小化)权值, 然后其特点就是当选的物品越多的时候权值越大(越小). 算法分析 我们先不考虑物品限制条件, 假定我们 ...

  8. CodeForces 125E MST Company

    E. MST Company time limit per test 8 seconds memory limit per test 256 megabytes input standard inpu ...

  9. WQS二分题集

    WQS二分,一种优化一类特殊DP的方法. 很多最优化问题都是形如“一堆物品,取与不取之间有限制.现在规定只取k个,最大/小化总收益”. 这类问题最自然的想法是:设f[i][j]表示前i个取j个的最大收 ...

随机推荐

  1. html实现邮箱发送邮件_js发送邮件至指定邮箱功能

    在前端开发中,JavaScript并没有提供直接操作Email邮箱的功能方法,但是遇到这样的需求,我们应该如何实现js发送邮件至指定邮箱功能呢?下面列举能够在通过前端实现邮件发送的几种方式: 方式一: ...

  2. 关于npm和yarn 安装vue脚手架

    第一篇博客有点小紧张.轻喷~ 第一步:安装node.js       地址 --------https://nodejs.org/en/ 详细步骤这里就不写了    可以去看     地址 ----- ...

  3. Mac搭建svn服务器环境

    Mac搭建svn服务器环境 svn是Subversion的简称,是一个开放源代码的版本控制系统, Mac系统自带了svn的服务端和客户端功能, 因此不需要下载第三方软件,就可以支持svn进行版本的管控 ...

  4. 从零开始使用 Webpack 搭建 Vue3 开发环境

    从零开始使用 Webpack 搭建 Vue3 开发环境 创建项目 首先需要创建一个空目录,在该目录打开命令行,执行 npm init 命令创建一个项目,这个过程会提示输入一些内容,完成后会自动生成一个 ...

  5. day59 pip安装django出错解决方案

    在虚拟环境下,输入 pipinstall django ==2.2,安装django,可能会出现超时问题 ​ 这里的报错是网络问题,解决方案有如下三种 (1)多试几次,网络好就装上了 (2)Cmd输入 ...

  6. 微信小程序开发中遇到的几个小问题

    本地图片不显示,开发工具运行是没问题的,但真机调试却显示不了 item.img = '/goods/img/图片.png' <image src="{{item.img}}" ...

  7. python数据处理(六)之数据清洗:标准化和脚本化

    1.数据归一化和标准化 a. 归一化:对数据集进行计算,使数据都位于一个特定的范围\ b.标准化: c.删除离群值 2.数据存储 a.保存到SQLite数据库中 b.导出到简单的文件中csv 3.找到 ...

  8. 机器学习实战基础(二十二):sklearn中的降维算法PCA和SVD(三) PCA与SVD 之 重要参数n_components

    重要参数n_components n_components是我们降维后需要的维度,即降维后需要保留的特征数量,降维流程中第二步里需要确认的k值,一般输入[0, min(X.shape)]范围中的整数. ...

  9. 前端06 /JavaScript之BOM、DOM

    前端06 /JavaScript 目录 前端06 /JavaScript 昨日内容回顾 js的引入 js的编程要求 变量 输入输出 基础数据类型 number string boolean null ...

  10. 【Python】关于如何判断一个list是否为空的思考

    前言 今天随手翻 stackoverflow,看到问题叫 How do I check if a list is empty? 一看这个问题,不难猜到到这是一个刚学 Python 的人提问的,因为这个 ...