【学习笔记】WQS二分
介绍
WQS二分一般用于处理一类带限制的题目,如恰好选
举个例子,我们现在有一个上凸的函数,根据定义有它的斜率单调递减。此时考虑二分斜率
那么,如何求被切点呢?我们先考虑这个函数的意义,
假设我们最优答案的斜率为
有的时候我们会发现某个斜率
例题
[国家集训队] Tree I
经典例题。
设
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N=5e4+5,M=1e5+5;
struct edge{int u,v,w,col;}e[M];
bool cmp(edge x,edge y){return(x.w!=y.w?x.w<y.w:x.col<y.col);}
class DSU
{
private:
int fa[N];
int find(int x)
{
if(fa[x]==x)return x;
return fa[x]=find(fa[x]);
}
public:
void init(){for(int i=1;i<N;i++)fa[i]=i;}
void merge(int x,int y){fa[find(x)]=find(y);}
bool same(int x,int y){return find(x)==find(y);}
}dsu;
int n,m;
pair<int,int>check(int k)
{
for(int i=1;i<=m;i++)
if(e[i].col==0)e[i].w+=k;
sort(e+1,e+m+1,cmp);
int res=0,cnt=0;
dsu.init();
for(int i=1;i<=m;i++)
{
int u=e[i].u+1,v=e[i].v+1,w=e[i].w,col=e[i].col;
if(!dsu.same(u,v))res+=w,cnt+=(col==0?1:0),dsu.merge(u,v);
}
for(int i=1;i<=m;i++)
if(e[i].col==0)e[i].w-=k;
return make_pair(res,cnt);
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int need;
cin>>n>>m>>need;
for(int i=1;i<=m;i++)cin>>e[i].u>>e[i].v>>e[i].w>>e[i].col;
int l=-101,r=101;
while(l+1<r)
{
int mid=l+r>>1;
if(check(mid).second>=need)l=mid;
else r=mid;
}
cout<<check(l).first-need*l;
return 0;
}
最小度限制生成树
设
注意本题需要判断无解的情况,若连接
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N=5e4+5,M=5e5+5;
struct edge{int u,v,w;}e[M];
class DSU
{
private:
int fa[N];
int find(int x)
{
if(fa[x]==x)return x;
return fa[x]=find(fa[x]);
}
public:
void init(){for(int i=1;i<N;i++)fa[i]=i;}
void merge(int x,int y){fa[find(x)]=find(y);}
bool same(int x,int y){return find(x)==find(y);}
}dsu;
int n,m,s;
bool cmp(edge x,edge y){return x.w<y.w||x.w==y.w&&(x.v==s||x.u==s);}
pair<int,int>check(int k)
{
for(int i=1;i<=m;i++)
if(e[i].u==s||e[i].v==s)e[i].w-=k;
sort(e+1,e+m+1,cmp);
int res=0,cnt=0;
dsu.init();
for(int i=1;i<=m;i++)
{
int u=e[i].u,v=e[i].v,w=e[i].w;
if(!dsu.same(u,v))res+=w,cnt+=(u==s||v==s?1:0),dsu.merge(u,v);
}
for(int i=1;i<=m;i++)
if(e[i].u==s||e[i].v==s)e[i].w+=k;
return make_pair(res,cnt);
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int need,cnt=0;
cin>>n>>m>>s>>need;
for(int i=1;i<=m;i++)
{
cin>>e[i].u>>e[i].v>>e[i].w;
if(e[i].u==s||e[i].v==s)cnt++;
}
int l=-3e4-1,r=3e4+1;
if(check(l).second>need||cnt<need)cout<<"Impossible";
else
{
while(l+1<r)
{
int mid=l+r>>1;
if(check(mid).second<=need)l=mid;
else r=mid;
}
if(check(l).second<need)l++;
cout<<check(l).first+need*l;
}
return 0;
}
忘情
化简题目的式子可以得到
若我们不考虑分的段数,有DP转移方程如下:
其中
设
二分斜率
接下来分析DP的部分。设
假设当前的
先列出转移方程:
不等式两边同时除以
设
也就是说当