WQS二分入门 & LuoguP2619 Tree I
题意
题意简化: 给定一个带权无向图,边分为黑白两色,求白边数量为k时的最小生成树
k<=n<=5∗104,m<=105
Solution
WQS二分
此题作为WQS的入门题, 先得讲讲WQS二分
先谈谈自己的感性理解:
WQS二分主要用于: 消除求解最值问题中的物品个数限制
主要思想: 通过对于每个物品加固定权值通过最大(最小)的约束,不断逼近(达到)要求的物品个数限制
使用前提: 对于物品选取个数 Xi, 所对应的答案 Yi 需要满足( Xi,Yi ) 能在平面上组成凸包
通常对于凸性的证明或寻找,可采取以下几种方式:
- 暴力打表
- 感性理解(即:物品选的越多/块数分的越多,答案必定越大/越小)
建议在有了感性认知之后,能有理性的对于算法本质的理解
推荐博客:https://blog.csdn.net/a_forever_dream/article/details/105581221
LuoguP2619 Tree I
对于此题而言
若我们将每条选的白边都加上某个权值,则BST排序后白边会靠后
即: 选的白边数量变少,且黑边内部与白边内部边权相对关系并不发生变化
所以考虑二分附加权值,每次将白边-附加权,在当前的最小生成树中统计白边数量cnt
若 cnt>=k: 则说明此时白边选多了, 需要增大附加权
否则: 减小附加权
最后再在答案中将附加权消除即可
code
#include<bits/stdc++.h>
using namespace std;
#define re register
#define in inline
#define get getchar()
#define ll long long
in int read()
{
int t=0; char ch=get;
while(ch<'0' || ch>'9') ch=get;
while(ch<='9' && ch>='0') t=t*10+ch-'0', ch=get;
return t;
}
const int _=1e5+23;
struct edge{
int u,v,val;
}b[_],w[_],e[_];
int tot1,tot2,n,m,lim,vis[_],fa[_];
in int cmp(edge a,edge b)
{ return a.val<b.val; }
in int find(int x)
{ return fa[x]==x ? fa[x] : fa[x]=find(fa[x]);}
in int check(int x,int &sum)
{
int s1=1,s2=1;
// 此题黑白边相对顺序不会改变,可以归并排序
for(re int i=1;i<=m;++i)
{
if(w[s2].val+x<=b[s1].val) e[i]=w[s2],vis[i]=1,e[i].val+=x,s2++;
else e[i]=b[s1],vis[i]=0,s1++;
if(s2>tot2) {
for(re int j=s1;j<=tot1;++j) ++i,e[i]=b[j],vis[i]=0;
break;
}
if(s1>tot1) {
for(re int j=s2;j<=tot2;++j) ++i,e[i]=w[j],vis[i]=1,e[i].val+=x;
break;
}
}
int used=0;
for(re int i=1;i<=n;++i) fa[i]=i;
for(re int i=1;i<=m;++i)
{
int u=e[i].u, v=e[i].v;
int fx=find(u), fy=find(v);
if(fx==fy) continue;
fa[fx]=fy;sum+=e[i].val;
used+=vis[i];
}
return used;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
#endif
n=read(), m=read(), lim=read();
for(re int i=1;i<=m;++i)
{
int x=read()+1, y=read()+1, z=read(), o=read();
if(o==1) b[++tot1].u=x, b[tot1].v=y, b[tot1].val=z;
else w[++tot2].u=x, w[tot2].v=y, w[tot2].val=z;
}
sort(b+1,b+tot1+1,cmp), sort(w+1,w+tot2+1,cmp);
int l=-120, r=120,k,ans;
while(l<=r)
{
int mid=l+r>>1,sum=0;
int used=check(mid,sum);
if(used>=lim) l=mid+1, ans=sum-lim*mid;
// 注意这里是大于等于时就要统计答案,即可能在凸包上出现三点共线
else r=mid-1;
}
cout<<ans<<endl;
}
嗯,就这样了...
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 现代计算机视觉入门之:什么是图片特征编码
· .NET 9 new features-C#13新的锁类型和语义
· Spring AI + Ollama 实现 deepseek-r1 的API服务和调用
· 《HelloGitHub》第 106 期
· 数据库服务器 SQL Server 版本升级公告
· 深入理解Mybatis分库分表执行原理
· 使用 Dify + LLM 构建精确任务处理应用
2019-04-04 leo101
2019-04-04 Luogu P4064 [JXOI2017]加法
2019-04-04 luogu P4823 [TJOI2013]拯救小矮人
2019-04-04 Luogu P4643 阿狸和桃子的游戏
2019-04-04 Luogu P4105 [HEOI2014]南园满地堆轻絮
2019-04-04 Luogu P3602 Koishi Loves Segments