题面与大意
题面很难看
大意:有一堆矩形的土地,你可以分组购买它们,每一组购买的价格是这些矩形中最大的长乘上最大的宽,问你最佳购买方案
题外话:FJ是个什么东西?
O ( n 2 ) O(n^2) O(n2)做法
这道题的普通打法是最难推的一部分
首先我们可以想到:像下面的这种情况
完全可以买一送一啊!
那我们就按长度从小到大对它们进行排序,然后将排在前面而宽度又比较小的删掉。
然后我们会发现高度是单调递增、宽度单调递减的
用
f
i
f_i
fi表示买前
i
i
i片土地的最小花费
易得方程:
f
i
=
f
j
+
l
i
w
j
+
1
f_i=f_j+l_iw_{j+1}
fi=fj+liwj+1
可以得60分诶!!
这数据也忒水了
优化
一开始删除土地捆绑销售时我用了
O
(
n
2
)
O(n^2)
O(n2)的打法
但其实在这里我们可以用单调栈的思想实现
O
(
n
)
O(n)
O(n)排查。
然后套斜率:
f
i
=
f
j
+
l
i
w
j
+
1
f_i=f_j+l_iw_{j+1}
fi=fj+liwj+1
f
i
−
l
i
w
j
+
1
=
f
j
f_i-l_iw_{j+1}=f_j
fi−liwj+1=fj
f
j
=
−
l
i
w
j
+
1
+
f
i
f_j=-l_iw_{j+1}+f_i
fj=−liwj+1+fi
其中
y
=
f
j
y=f_j
y=fj
k
=
l
i
k=l_i
k=li
x
=
−
w
j
+
1
x=-w_{j+1}
x=−wj+1
b
=
f
i
b=f_i
b=fi
(注意要保持
k
k
k与
x
x
x的单调增)
数!形!结!合!大!法!好!
上代码:
tips:要注意范围什么的,主要是
j
j
j后面要不要
+
1
+1
+1的问题
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
struct deque//Especially for monotone queue
{
int head,tail;//head~tail
/*/
int list[110];//convinient for debug
deque():head(1),tail(0){}
/*/
int *list;//avoid the stack-crashing
deque():head(1),tail(0),list(new int[51000]){}
//*/
int size(){return tail-head+1;}
int front(){return list[head];}
void push_front(int x){list[--head]=x;}
void pop_front(){++head;}
int front_2nd(){return list[head+1];}
int back(){return list[tail];}
void push_back(int x){list[++tail]=x;}
void pop_back(){--tail;}
int back_2nd(){return list[tail-1];}
};//手打双端队列吼啊!
struct field
{
int l,w;//length & width
bool operator < (field b)const
{return l<b.l||l==b.l&&w<b.w;}
}a[51000],b[51000];
int top;
ll f[51000];
#define Y(j) f[j]
#define k(i) b[i].l
#define X(j) -b[j+1].w
double slope(int c,int d)
{return (Y(c)-Y(d))/(double)(X(c)-X(d));}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d%d",&a[i].l,&a[i].w);
sort(a+1,a+n+1);
b[top=1]=a[1];
for(int i=2;i<=n;i++)
{
while(top&&b[top].w<=a[i].w)--top;//还有不要让top变成负数
b[++top]=a[i];
}
memset(f,1,sizeof(f));
f[0]=0;
deque q;
q.push_back(0);
for(int i=1;i<=top;i++)
{
while(q.size()>1&&slope(q.front(),q.front_2nd())<k(i))q.pop_front();
f[i]=f[q.front()]+1ll*b[i].l*b[q.front()+1].w;
//1ll是指在long long类型下的1,用于将计算得到值强行转换为long long
while(q.size()>1&&slope(q.back_2nd(),q.back())>slope(q.back(),i))q.pop_back();
q.push_back(i);
}
printf("%lld",f[top]);
return 0;
}