wqs 二分
1.1 wqs 二分简介
现在有一个函数 是凸的,需要求出其在 处的点值。
我们先假设它是一个下凸壳。
我们考虑二分斜率 ,用一条斜率为 的直线去切这个凸包,也即求 以及取到这个点的 。
如上图,发现取到该点的 是比 大的,于是减小二分的斜率 ,直到找到 。
我们把我们要求的东西变成了 次 。这个式子在有些情况下比较好求:
考虑这样的问题:有 个物品,求恰好选择 个的情况下的最小代价。如果我们二分的时候将每一个物品的代价减少 ,再求一个最优解,那么就可以得到 。可能加上 的限制之后不好做,但是去掉之后就很水,这时候可以用 wqs 二分。
wqs 二分就是这样的过程。在上面的问题中,凸性就是 一定成立。这个条件满足的场景就是例如你选择两个物品,如果从 到 选择了某一个物品那么不如在 到 之间选择。
1.2 一些细节
正常情况下,如果 是整数,那么 是整数,因此二分斜率只需在整数域内二分即可。对于小数运算,可能需要在实数域内二分。实数域内二分,还是写 次二分好一些,而不是 。后者容易死循环。
对于三点共线需要认真考虑。我们考虑这样的情形:
虽然我们不一定切到 ,但是我们一定会切到这条直线上的某一个点。这条直线都对应着唯一的截距,所以我们得到 的时候只需要令答案为 (就是这条直线的截距经过 坐标的点)即可,而不是 。
但是你要注意二分到实际斜率的时候程序会切到哪一个点。例如,如果我们钦定相同答案取最小个数的操作方案(也即,取最小的 使得经过 的某一个线段斜率为 )那么当你二分到等于直线斜率的 的时候,你会得到点 。而你二分到 的时候你会得到点 。因此如果你得到的 ,那么你需要把答案更新(赋值)为 ;否则你不能更新答案。
最后,wqs 二分的条件是 而不是 ,否则取不到 处的答案!
- 如果你的 dp 写挂了导致没法保证贴任何一边,还有一种方法:实数域上二分,如果 是小数不和任何一条直线相交,那么一定会贴在某一边上。这样你可以二分到确切的 。
- 一般用一个 pair 来存答案和用了几个,贴左边的话要重定义一下 cmax,但是如果你不小心写了个 max,那就寄了。
P2619 Tree I
【题意】
有一张无向图,有一些黑边和白边,要求选择一个生成树,使得恰好有 个白边的前提下,边权和最小。
【分析】
考虑令 为有 个白边的情况下的边权和。 其实是一个下凸壳。
考虑解决 的问题,其实就是给每条白边边权减去 之后求一个最小生成树,再取其白边个数作为 。这个是好做的。
如果我们钦定黑边比白边先选,那么就可以钦定相同答案取最小个数的操作方案。
struct edge {
int s, t, c;
}e[2][100010];
int ans; int cnt[2]; int fa[50010]; int n, m, k;
int get(int x) {if(fa[x] == x) return x; else return fa[x] = get(fa[x]); }
void merge(int x, int y) {x = get(x); y = get(y); fa[x] = y; }
pii getans(int mid) {
f(i, 0, n) fa[i] = i;
int sum = 0, dot = 0;
for(int l = 1, r = 1; l <= cnt[0] || r <= cnt[1]; ) {
if(l <= cnt[0] && (r > cnt[1] || e[0][l].c - mid < e[1][r].c)) {
if(get(e[0][l].s) == get(e[0][l].t)) ;
else { merge(e[0][l].s, e[0][l].t); sum += e[0][l].c - mid; dot++; }
l ++;
}
else {
if(get(e[1][r].s) == get(e[1][r].t)) ;
else {merge(e[1][r].s, e[1][r].t); sum += e[1][r].c; }
r ++;
}
}
return {sum, dot};
}
signed main() {
cin >> n >> m >> k;
f(i, 1, m) {
int s, t, c, col; cin >> s >> t >> c >> col;
e[col][++cnt[col]] = {s, t, c};
}
auto cmp = [=](edge th, edge op) {return th.c < op.c; };
f(i, 0, 1) sort(e[i] + 1, e[i] + cnt[i] + 1, cmp);
int l = -100, r = 100;
while(l <= r) {
int mid = (l + r) >> 1;
pii res = getans(mid);
int p = res.second, b = res.first;
if(p > k) r = mid - 1;
else if(p == k) { ans = b + mid * k;break; }
else {l = mid + 1; ans = b + mid * k;}
}
cout << ans << endl;
}
1.3 优化 dp
CF1832F. Zombies
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~