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. HDU 5961 传递 题解

    题目 我们称一个有向图G是 传递的,当且仅当对任意三个不同的顶点a,,若G中有 一条边从a到b且有一条边从b到c ,则G中同样有一条边从a到c. 我们称图G是一个 竞赛图,当且仅当它是一个有向图且它的 ...

  2. Emergency Evacuation 题解

    The Japanese government plans to increase the number of inbound tourists to forty million in the yea ...

  3. day72 bbs项目☞登录注册

    目录 一.表创建及同步 二.注册功能 二.登录页面搭建 一.表创建及同步 from django.db import models from django.contrib.auth.models im ...

  4. day30 继承、派生与多态,类中方法和内置函数

    目录 一.多继承出现的问题(mixins机制) 二.派生与方法重用 三.多态 1 什么是多态 2 为什么要有多态 3 python中多态的鸭子类型 四.绑定方法与非绑定方法 1 绑定方法 1.1对象的 ...

  5. shell进阶篇之数组应用案例

    数组中可以存放多个值. Shell 只支持一维数组(不支持多维数组),初始化时不需要定义数组大小. 与大部分编程语言类似,数组元素的下标由0开始. Shell 数组用括号来表示,元素用"空格 ...

  6. SQL批量插入数据【万级】

    1.每4000条插入一次 for (int i = 0; i < dt.Rows.Count; i++) { IsTBProductForStockInfo model = new IsTBPr ...

  7. java 面向对象(三):类结构 属性

    类的设计中,两个重要结构之一:属性 对比:属性 vs 局部变量 1.相同点: * 1.1 定义变量的格式:数据类型 变量名 = 变量值 * 1.2 先声明,后使用 * 1.3 变量都其对应的作用域 2 ...

  8. Linux03 /shell命令

    Linux03 /shell命令 目录 Linux03 /shell命令 1. xshell快捷键 2. 修改linux的命令提示符,通过变量PS1控制 3. yum安装/卸载 4. cat命令 5. ...

  9. oop的三种设计模式(单例、工厂、策略)

    参考网站 单例模式: 废话不多说,我们直接上代码: <?php /** 三私一公 *私有的静态属性:保存类的单例 *私有的__construct():阻止在类的外部实例化 *私有的__clone ...

  10. equals方法与==关系的总结

    /** * ==&&equals区别 * * ==比较的是栈内存中的值 * 对于基本类型数据,比较的是栈内存中的值 * 对于引用数据类型,比较的是栈内存中的值(值的真是含义是一个地址) ...