wqs二分

引入

一般问题:用于解决 n 个物品,恰好选 m 个 ,最大化或最小化权值和

用 wqs 的题目特点

1、题目可以用这样的 dp 求解 :dp[i][j] 表示 前 i 个 选 j 个的最优策略,

                                 dp[i][j]=max/min { dp[k][j-1] + cost(k , i) } ,复杂度不管怎么优化都为平方级别

2、若没有恰好选 m 个的限制会很容易用 dp 求解

3、设 f(i) 为选 i 个物品的最佳答案,函数 f 的图像为一个凸包

tips:做题时可感性理解直接尝试,毕竟码量还好

思路

对于 wqs 二分我个人采用两种同时理解,一种方便理解,一种方便写代码

先尝试理解,已知选 i 个时的最优值 f(i) 构成凸包。假设他是一个上凸包。

横坐标为选 x 个,纵坐标为选 x 个时最优值,显然答案为 f(m) ,但我们得不到这个凸包

考虑用一条斜率为 k 的直线去切这个凸包(二分这个 k ),发现

1、当这条直线与凸包相切时,可以得到选 不知道多少个 时的最优值

2、对于所有斜率为 k 且与凸包有交点的直线,其截距是最大的

对于第一点,意味着我们可以通过调整 k 来切到点( m , f(m) )

对于第二点,意为着我们可以得到这条切线,怎么做呢?

        假设斜率为k的凸包的切线为 y = kx + b ,由于 b = y - kx ,所以

        对于斜率为k 且与凸包有交点的直线的截距 b ,有 b(x) =  f(x) - kx

发现这个柿子等价于把每次选择时的权值和先 -=k ,此时就没有 恰好 m 的限制了

用一个线性的 dp 可同时知道 b 的最值和此时 x 的值 ,然后返回判断二分

写代码时可以理解为

给每一次选择(可能选一个也可能很多个,看题目)一个附加权值,将每次操作(一般是每个点)点权减这个权值,然后就可以不管限制个数,直接做,最后把答案加上 m * 附加权值 

例题分析

P3620

考虑暴力dp, 𝑓[𝑖][𝑗][0/1] 为前 i 个房子,连了 j 条线,并且第 i 个房子是否和第 i−1 个房子连线。

转移很简单, 𝑓[𝑖][𝑗][0]=min⁡(𝑓[𝑖−1][𝑗][0],𝑓[𝑖−1][𝑗][1]) ,𝑓[𝑖][𝑗][1]=𝑓[𝑖−1][𝑗−1][0]+x[𝑖]−x[𝑖−1]

发现空间爆炸

设 f[x] 为连 x 条线的最优答案,易发现为上凸包,考虑 wqs 二分 ,枚举一个附加权值,即可以去掉 j 这一维,就做完了。

代码

#include<bits/stdc++.h>
using namespace std;
#define N 101000
#define int long long
int f[N][2],g[N][2],n,k,x[N],ans;
#define inf 1LL<<60
void lc(int &x,int &y,int xx,int yy){
	if(x>xx||(x==xx&&y>yy)) x=xx,y=yy;
}
void solv(int mid){
	f[1][0]=g[1][0]=0;
	f[1][1]=inf;
	for(int i=2;i<=n;i++){
		g[i][1]=g[i-1][0]+1,f[i][0]=f[i-1][0],g[i][0]=g[i-1][0];
		lc(f[i][0],g[i][0],f[i-1][1],g[i-1][1]);
		f[i][1]=f[i-1][0]+x[i]-x[i-1]-mid;
	}
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	cin>>n>>k;
	for(int i=1;i<=n;i++){
		cin>>x[i];
	}
	int l=0,r=1e9+1;
	while(l<r){
		int mid=(l+r)>>1;
		int a1=inf,a2=0;
		solv(mid);
		lc(a1,a2,f[n][0],g[n][0]);
		lc(a1,a2,f[n][1],g[n][1]);
		if(a2>k) r=mid;
		else{
			ans=a1+mid*k,l=mid+1;
		}
	}
	cout<<ans;
	return 0;
}

 下周考完whk继续写......

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值