题目链接:
https://www.lydsy.com/JudgeOnline/problem.php?id=1597
看到题目,时间复杂度至少是
O
(
N
l
o
g
N
)
O(NlogN)
O(NlogN)或
O
(
N
s
q
r
t
(
N
)
)
O(Nsqrt(N))
O(Nsqrt(N))
前者的概率较大,此题有计算内乘积,应该与分块无关(早就知道是斜率优化)
假设长为
a
a
a值,宽为
b
b
b值
观察题目, 可以猜想一下决策会不会有连续性,猜想决策可能与排序有关,先按长 (
a
a
a) 排序。在思考一会,就会发现该猜想是对的,因为每个总方案中,如果存在一组土地
a
a
a值不连续,那该方案一定不是唯一的最优解。
证明:假设存在一个方案使得将土地按 a a a值升序排列后存在 i , j , k i,j,k i,j,k使得 a i < a j < a k a_{i}<a_{j}<a_{k} ai<aj<ak且 i , k i,k i,k分在一组中,而 j j j在另一组中。若 b j < b i b_j<b_i bj<bi,则将 i , j , k i,j,k i,j,k分在一组肯定会更优,因为其他不变,把 j j j移动到 i , k i,k i,k这一组, j j j原来的那组值会更小,而 i , j , k i,j,k i,j,k这一组不会变,因为 j j j的长和宽在这一组中均不为最大值。同理,可证明另一种情况。(有兴趣可以自行证明)
这样,我们就可以先按 a a a值排序。又因为如果某一块土地 b b b值比任何一块在它右边的土地小,那么一个最优解一定是将这块土地与那块土地合并,因为这样不会产生任何影响,就可以去除满足这个条件的土地
在这一步后,整个数组也就是
a
a
a值递增,
b
b
b值递减,那么简单看一下就知道可以dp了
设
f
[
i
]
f[i]
f[i]为前
i
i
i块土地的最小花费
方程就是
f
[
i
]
=
f
[
j
]
+
b
[
j
+
1
]
∗
a
[
i
]
f[i]=f[j]+b[j+1]*a[i]
f[i]=f[j]+b[j+1]∗a[i]
看到乘积,自然想到斜率优化,
p
i
(
−
b
[
j
+
1
]
,
f
[
i
]
)
,
k
i
=
a
[
i
]
p_{i}(-b[j+1],f[i]),k_{i}=a[i]
pi(−b[j+1],f[i]),ki=a[i]
就可以愉快的斜率优化了,时间复杂度
O
(
N
)
O(N)
O(N)
C o d e : Code: Code:
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int MAXN=50000;
struct w{
int a,b;
}a[MAXN+10],b[MAXN+10];
struct po{
int x,y;
}s[MAXN+10];
int f[MAXN+10],q[MAXN+10],fr,ed;
inline bool cmp(const w&,const w&);
inline bool cmp_slope(po,po,int);
inline bool cmp_slope2(po,po,po);
inline int read();
signed main(){
freopen ("1597.in","r",stdin);
freopen ("1597.out","w",stdout);
int n=read();
for (register int i=1;i<=n;++i){
a[i].a=read();
a[i].b=read();
}
stable_sort(a+1,a+n+1,cmp);
for (register int i=n;i>=1;--i){
if (a[i].b > a[q[fr]].b) q[++fr]=i;
}
n=fr; fr=0;
for (register int i=1;i<=n;++i) b[i]=a[q[n-i+1]];
for (register int i=1;i<=n;++i) a[i]=b[i];
s[0].y=0; s[0].x=-a[1].b;
for (register int i=1;i<=n;++i){
int k=a[i].a;
while (fr>ed && cmp_slope(s[q[ed]],s[q[ed+1]],k)) ++ed;
f[i]=f[q[ed]]-s[q[ed]].x*a[i].a;
s[i].y=f[i]; s[i].x=-a[i+1].b;
while (fr>ed && cmp_slope2(s[q[fr-1]],s[q[fr]],s[i])) --fr;
q[++fr]=i;
}
printf("%lld\n",f[n]);
return 0;
}
inline int read(){
int x=0;
char c=getchar();
while (!isdigit(c))c=getchar();
while (isdigit(c))x=(x<<3)+(x<<1)+(c&15),c=getchar();
return x;
}
inline bool cmp(const w &x,const w &y){
return x.a<y.a;
}
inline bool cmp_slope(po x,po y,int k){
return ((double)(y.y-x.y))/((double)(y.x-x.x)) <= (double)k;
}
inline bool cmp_slope2(po x,po y,po z){
return ((double)(y.y-x.y))/((double)(y.x-x.x)) >= ((double)(z.y-y.y))/((double)(z.x-y.x));
}