wqs-二分
适用范围
wqs 可以解决的问题具体来说是这样:给你一些物品,我们可以从里面选的物品是有限的,每个物品有其价值,要求最大化这个价值,价值有可能为负。
抽象来说,就是给你一些物品,有一些限制,如果没有限制的做法很简单,要求求一个最优值。
问题解决
很显然,我们可以有一个 dp,
令
其实如果问题改一改,变成下凸包,wqs 二分也可以做,下面的例题就是一个下凸包,这里先以上凸包作为例子。
既然是上凸包,那么满足斜率递减,所以我们去二分这个斜率,很显然,不管二分的斜率是什么,都会切凸包于一点,假设这个点的横坐标是
如果我们能够求出这个点的纵坐标,我们就可以知道这个点对应的最优值是什么,进而能够得出答案。
那么现在的问题转化成了如果求这个点。假设当前的斜率为
注意:按道理说这里的每一个点都应该是整点,但如果都是整点太不好看了,于是就这样画了。
目前
我们令
事实上:
这并不是很好理解,我们来论证一下这个事情。
首先一个基本的事实是:根据
我们首先把所有的物品都减去
我再换一个角度解释一下:首先显然的一点是
那么我们就可以这样求
所以总复杂度应该是
细节处理
我们把刚刚的图再放下来。
你会发现这里有很多切不到的点,比如说点
例题
用
感性理解一下,如果我们直接求最小生成树,假如这颗树里一共有
下凸壳斜率递增,我们去二分这个斜率,设当前二分的值为
我们考虑如何计算
不过如果你真的这样做,结果可能回事全 WA ,为什么,原因就是出现了上述情况。
我来说一下我是如何避免的:
这是一个下凸壳,且因为都是整点,且点与点之间相隔为
读者就当所有点都是整数
假如说我们想求
我们这样做:求最小生成树我们排序时,默认相同权值下,白边在前,也就是说我们让他选的白边尽量多,所以当斜率恰好是
同时我们二分是这样做,我们让他二分时停在白边数量大于
放下代码理解一下:
check
:
inline int check(int mid){
ans=0;
for(int i=0;i<=V;i++) fa[i]=i;
for(int i=1;i<=E;i++) if(li[i].col==0) li[i].w-=mid;
sort(li+1,li+E+1);
int cnt=0,cnt2=0;
for(int i=1;i<=E;i++){
int from=li[i].from,to=li[i].to;
int faf=find(from),fat=find(to);
if(faf!=fat){
if(li[i].col==0) cnt++;
fa[faf]=fat;ans+=li[i].w;
cnt2++;if(cnt2==V-1) break;
}
}
// ans+=mid*cnt;
for(int i=1;i<=E;i++) if(li[i].col==0) li[i].w+=mid;
return cnt;
}
二分:
while(l<r){
int mid=(l+r)>>1;
if(check(mid)<need) l=mid+1;
else r=mid;
}
如果我们最终要求的是 check(l)
,这样就相当于把切 need*cnt
就可以了。
代码:
#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 50100
#define M 200010
using namespace std;
const int INF=0x3f3f3f3f;
template<typename T> inline void read(T &x) {
x=0; int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
x*=f;
}
struct edge{
int to,from,w,col;
inline void intt(int to_,int from_,int w_,int col_){
to=to_;from=from_;w=w_;col=col_;
}
inline bool operator < (const edge &b)const{
if(w!=b.w) return w<b.w;
return col<b.col;
}
};
edge li[M];
int tail;
int V,E,need,ans;
inline void add(int from,int to,int w,int col){
li[++tail].intt(to,from,w,col);
}
int fa[N];
inline int find(int k){
return fa[k]==k?k:fa[k]=find(fa[k]);
}
inline int check(int mid){
ans=0;
for(int i=0;i<=V;i++) fa[i]=i;
for(int i=1;i<=E;i++) if(li[i].col==0) li[i].w-=mid;
sort(li+1,li+E+1);
int cnt=0,cnt2=0;
for(int i=1;i<=E;i++){
int from=li[i].from,to=li[i].to;
int faf=find(from),fat=find(to);
if(faf!=fat){
if(li[i].col==0) cnt++;
fa[faf]=fat;ans+=li[i].w;
cnt2++;if(cnt2==V-1) break;
}
}
// ans+=mid*cnt;
for(int i=1;i<=E;i++) if(li[i].col==0) li[i].w+=mid;
return cnt;
}
int main(){
freopen("my.in","r",stdin);
freopen("my.out","w",stdout);
read(V);read(E);read(need);
for(int i=1;i<=E;i++){
int from,to,w,col;
read(from);read(to);read(w);read(col);
add(from,to,w,col);
}
int l=-100,r=100;
while(l<r){
int mid=(l+r)>>1;
if(check(mid)<need) l=mid+1;
else r=mid;
}
check(l);
printf("%d\n",ans+need*l);
return 0;
}
wqs 二分与费用流
有一些题目难以证明凸性,但是如果其可以建立费用流模型,因为费用流函数,即
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!