题意
给
n
n
n 个木材,求制造
k
k
k 个木板浪费的木材的最小值,木材可以随意组合
制造木板浪费的木材:将
m
m
m 块木材连在一起,将所有的木材砍成一样的高度,砍掉的就是浪费量
思路
有分治优化,斜率优化,
w
q
s
wqs
wqs 二分,我写的斜率优化
d
p
dp
dp
先按高度从大到小排序
d
p
i
,
j
dp_{i,j}
dpi,j 表示前
i
i
i 个木材制成
k
k
k个木板的最小浪费量
s
u
m
i
sum_{i}
sumi 表示前
i
i
i 个木材总面积
s
u
m
w
i
sumw_{i}
sumwi 表示前
i
i
i 个木材的宽度
d
p
i
,
j
=
d
p
k
,
j
−
1
+
s
u
m
i
−
s
u
m
k
−
h
i
∗
(
s
u
m
w
i
−
s
u
m
w
k
)
dp_{i,j} = dp_{k,j-1} + sum_{i} - sum_{k} - h_{i} * (sumw_{i} - sumw_{k})
dpi,j=dpk,j−1+sumi−sumk−hi∗(sumwi−sumwk)
由于
d
p
i
,
j
dp_{i,j}
dpi,j 只与
d
p
k
,
j
−
1
dp_{k,j-1}
dpk,j−1 有关,因此只用记前一个状态就行,设当前状态为
s
t
a
t
e
state
state,则前一个状态为
1
−
s
t
a
t
e
1 - state
1−state
设
j
<
k
<
i
j < k < i
j<k<i 且
k
k
k 点比
j
j
j 点更优,易得
d
p
k
,
1
−
s
t
a
t
e
+
s
u
m
i
−
s
u
m
k
−
h
i
∗
(
s
u
m
w
i
−
s
u
m
w
k
)
<
=
d
p
j
,
1
−
s
t
a
t
e
+
s
u
m
i
−
s
u
m
j
−
h
i
∗
(
s
u
m
w
i
−
s
u
m
w
j
)
dp_{k,1-state} + sum_{i} - sum_{k} - h_{i} * (sumw_{i} - sumw_{k}) <= dp_{j,1-state} + sum_{i} - sum_{j} - h_{i} * (sumw_{i} - sumw_{j})
dpk,1−state+sumi−sumk−hi∗(sumwi−sumwk)<=dpj,1−state+sumi−sumj−hi∗(sumwi−sumwj)
即
d
p
k
,
1
−
s
t
a
t
e
−
s
u
m
k
+
d
p
j
,
1
−
s
t
a
t
e
−
s
u
m
j
s
u
m
w
k
−
s
u
m
w
j
<
=
−
h
i
\frac{dp_{k,1-state} - sum_{k} + dp_{j,1-state} - sum_{j}}{sumw_{k} - sumw_{j}} <= - h_{i}
sumwk−sumwjdpk,1−state−sumk+dpj,1−state−sumj<=−hi
设
y
i
=
d
p
k
,
1
−
s
t
a
t
e
−
s
u
m
k
y_{i} = dp_{k,1-state} - sum_{k}
yi=dpk,1−state−sumk,
x
i
=
s
u
m
w
k
x_{i} = sumw_{k}
xi=sumwk,
y
k
−
y
j
x
k
−
x
j
<
=
−
h
i
\frac{y_{k} -y_{j}}{x_{k} - x_{j}} <= - h_{i}
xk−xjyk−yj<=−hi
说明如果
k
k
k 为解,
j
,
k
,
i
j,k,i
j,k,i 应该是上凸的,则解集是下凸的,用一个单调队列维护解集
#include <cstdio>
#include <cstring>
#include <algorithm>
#define mem(x,y) memset(x,y,sizeof x)
#define fo(i,s,t) for(int i=s;i<=t;i++)
#define cl1(x,y,n) fo(c1_i,0,n-1) x[c1_i]=y
#define cl2(x,y,n,m) fo(c2_i,0,n-1) fo(c2_j,0,m-1) x[c2_i][c2_j]=y
#define inf (~0U>>2)
#define inff (long long)(inf<<1)*(long long)(inf<<1)
using namespace std;
const int maxn=5e3+10;
const int maxm=2e3+10;
const int mod=1e9+7;
int n,k,q[maxn],state=1;
long long sum[maxn],sumw[maxn],dp[maxn][2];
struct node{long long w,h;}a[maxn];
bool cmp(node _x,node _y)
{
return _x.h>_y.h;
}
///dp[i][j]=dp[k][j-1]+sum[i]-sum[k]-a[i].h*(sumw[i]-sumw[k])
long long x(int i)
{
return sumw[i];
}
long long y(int i)
{
return dp[i][1-state]-sum[i];
}
double slope(int i,int j)
{
return (y(j)-y(i))*1.0/(x(j)-x(i));
}
int main()
{
scanf("%d%d",&n,&k);
fo(i,1,n) scanf("%lld%lld",&a[i].w,&a[i].h);
sort(a+1,a+n+1,cmp);
sum[0]=sumw[0]=0;
fo(i,1,n)
{
sum[i]=sum[i-1]+a[i].w*a[i].h;
sumw[i]=sumw[i-1]+a[i].w;
dp[i][1]=sum[i]-sumw[i]*a[i].h;
}
fo(j,2,k)
{
int l=1,r=1;
q[1]=0;
state=1-state;
fo(i,1,n)
{
/**不卡精度可以用这个
while(l<r&&slope(q[l],q[l+1])<=-a[i].h)
l++;
dp[i][state]=dp[q[l]][1-state]+sum[i]-sum[q[l]]-a[i].h*(sumw[i]-sumw[q[l]]);
while(l<r&&slope(q[r-1],q[r])>=slope(q[r],i))
r--;
q[++r]=i;*/
while(l<r&&__int128_t(y(q[l+1])-y(q[l]))<=__int128_t(-a[i].h)*__int128_t(x(q[l+1])-x(q[l])))
l++;
dp[i][state]=dp[q[l]][1-state]+sum[i]-sum[q[l]]-a[i].h*(sumw[i]-sumw[q[l]]);
while(l<r&&__int128_t(y(q[r])-y(q[r-1]))*__int128_t(x(i)-x(q[r]))>=__int128_t(y(i)-y(q[r]))*__int128_t(x(q[r])-x(q[r-1])))
r--;
q[++r]=i;
}
}
printf("%lld\n",dp[n][k%2]);
return 0;
}