讲述人
通常我们需要求一些有个数限制的答案
令就是满足选个……所得到的答案
现在我们需要求解的是
如果将限制和结果映射到二位坐标系上,即
WQS能解决的前提是:所有点构成的是凸性的函数。(或者斜率[g(i)-g(i-1)]是单调的)
显然,我们不能直接求解
我们利用斜率是单调的,二分一个[一次函数]斜率
我们找到斜率在函数上切到的点的。通过和的大小关系调整k的二分范围。
然后就能找到切的即可算出
问题在于如何找到切到的节点。
画画图发现如果是上凸壳过该节点的直线的截距最大。下凸壳即找截距最小的点。
发现截距为
因此给每个物品的价值得到的值可以表示截距,然后在求整体最小(大)值,同时记录横坐标(选的个数)。
你会发现我们把有个数限制的问题转化为了没有个数限制的问题。
注意点
- 另为最优的决策点,如果多点斜率相同共线的情况下,即同一的是一个区间范围,具体情况具体分析,首先肯定有可能二分不到确切的,所以根据最优值相同第关键字返回最大或最小来判定结束条件,比如目标个数。
- 正常只要是整数,二分的斜率都不需要用小数。但注意可能会用到负数。
例题
[国家集训队]Tree I
因为i-1->i时,相当于少选最大的一条黑边,多选最小的一条白边。对于i和i-1的变化量,显然
显然是一个下凸壳了。
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int need,n,m,fa[N],tx;
double res;
struct edge {int x,y,col;double z;}E[N];
bool cmp(edge u,edge v) {return u.z<v.z;}
int gt_fa(int u) {return fa[u]==u?u:fa[u]=gt_fa(fa[u]);}
void kruskar(double k) {
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=1;i<=m;i++) if(!E[i].col)E[i].z-=k;
sort(E+1,E+1+m,cmp);
res=tx=0;
for(int i=1;i<=m;i++) {
int u=gt_fa(E[i].x),v=gt_fa(E[i].y);
if(u==v)continue;
fa[u]=v;
res+=E[i].z;tx+=!E[i].col;
}
for(int i=1;i<=m;i++) if(!E[i].col)E[i].z+=k;
}
double eps=1e-5;
void solve() {
double l=-100,r=100,ans=0;
while(r-l>eps) {
double mid=(l+r)/2;
kruskar(mid);
if(tx>=need) {ans=res+need*mid;r=mid;}
else l=mid;
}
printf("%.0lf\n",ans);
}
int main() {
scanf("%d%d%d",&n,&m,&need);
for(int i=1;i<=m;i++) scanf("%d%d%lf%d",&E[i].x,&E[i].y,&E[i].z,&E[i].col),E[i].x++,E[i].y++;
solve();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人