wqs 二分
前言#
WC 将我暴打。
wqs 二分#
黑龙江神仙 OIer 王钦石 归纳的一类二分方式,由于是 [IOI2016]ailens 的正解于是在国外被称为 "Alien trick"生动体现了 wqs 二分是多么魔法
考虑对于一类特殊的问题 :有限制的最优化。
抽象一个模型, 个数选出恰好 个,可能还有其他限制,最大化权值和。
令 表示选恰好 个时得到的最大价值,然后显然直接求所有方案是 的,类似背包那样的 DP 是平方的 ,但是如果画出 关于 的图像满足上凸性,就可以依照选择的数量进行特殊的加权,使用 wqs 二分。
同理最小化就要求是下凸的。
考虑首先二分斜率然后找到这个斜率的凸包的切线,因为图像上凸,斜率逐渐增大,切点就逐渐向右移。
于是这个过程是单峰的。
我们先不考虑斜率,先考虑求出一个斜率的时候如何求切点。
显然同一斜率时仅仅有切点使得 轴截距最小。
那么就是求每个物品贡献都减去了定值时的最优化问题,直接贪心/简单 DP 即可。
例题#
wqs 本人给出的一个例题。
首先对于使用白色边数量有限制,那么考虑对于所有白色边加/减一个权值使得恰好有要求的边数即可。
int n,m;
struct MST {
int st,ed,w,ty;
inline bool operator < (const MST &oth) const {
if(w != oth.w) return w < oth.w;
return ty > oth.ty;
}
}e[N << 1];
int dsu[N];
int find(int x) {
return x == dsu[x] ? x : dsu[x] = find(dsu[x]);
}
inline pii Kruskal(int x,int mx = INF) {
rep(i,1,m) e[i].w += e[i].ty * x;
std::sort(e + 1,e + m + 1);
repl(i,0,n) dsu[i] = i;
int cnt = 0,ans = 0,et = 0;
rep(i,1,m) {
int fx = find(e[i].st);
int fy = find(e[i].ed);
if(fx == fy) continue;
if(e[i].ty && !mx) continue;
mx -= e[i].ty;
dsu[fx] = fy;
cnt += e[i].ty;
ans += e[i].w;
if(++et == (n - 1))
break;
}
rep(i,1,m) e[i].w -= e[i].ty * x;
return std::make_pair(cnt,ans);
}
int main() {
init_IO();
n = read(),m = read();int ne = read();
rep(i,1,m) {
e[i].st = read();
e[i].ed = read();
e[i].w = read();
e[i].ty = 1 - read();
}
int l = -101,r = 101,ans = 0;
while(l <= r) {
int mid = (l + r) >> 1;
pii res = Kruskal(mid);
if(res.first >= ne) {
ans = res.second - mid * ne;
l = mid + 1;
}
else r = mid - 1;
}
write(ans),enter;
end_IO();
return 0;
}
概念差不多的一个题。
最开始我有那么 1 秒认为是对每个点都指定度了,吓我一跳
为一端为 的边赋予权值即可,甚至特判比上一个题要少。
然后就是特判一下无解就行。
int n,m;
struct MST {
int st,ed,w,ty;
inline bool operator < (const MST &oth) const {
if(w != oth.w) return w < oth.w;
return ty > oth.ty;
}
}e[M];
int dsu[N];
int find(int x) {
return x == dsu[x] ? x : dsu[x] = find(dsu[x]);
}
inline pii Kruskal(int x,int mx = INF) {
rep(i,1,m) e[i].w += e[i].ty * x;
std::sort(e + 1,e + m + 1);
rep(i,1,n) dsu[i] = i;
int cnt = 0,ans = 0,et = 0;
rep(i,1,m) {
int fx = find(e[i].st);
int fy = find(e[i].ed);
if(fx == fy) continue;
if(e[i].ty && !mx) continue;
mx -= e[i].ty;
dsu[fx] = fy;
cnt += e[i].ty;
ans += e[i].w;
if(++et == (n - 1))
break;
}
rep(i,1,m) e[i].w -= e[i].ty * x;
return std::make_pair(cnt,ans);
}
int main() {
init_IO();
n = read(),m = read();int s = read(),ne = read();
rep(i,1,m) {
e[i].st = read();
e[i].ed = read();
e[i].w = read();
e[i].ty = (e[i].st == s || e[i].ed == s);
}
int l = -30005,r = 30005,ans = 0;
if(Kruskal(l).first < ne || Kruskal(r).first > ne) {
putStr("Impossible");
end_IO();
return 0;
}
while(l <= r) {
int mid = (l + r) >> 1;
pii res = Kruskal(mid);
if(res.first >= ne) {
ans = res.second - mid * ne;
l = mid + 1;
}
else r = mid - 1;
}
write(ans),enter;
end_IO();
return 0;
}
作者:AstatineAi
出处:https://www.cnblogs.com/AstatineAi/p/wqs-binary-search.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本