[BZOJ] 1597: [Usaco2008 Mar]土地购买

记一次\(O(nlogn)\)吊锤\(O(n)\)实录

\(f[i]\)表示前\(i\)块土地,第\(i\)块必选的最小代价

为了方便转移,按\(h\)为第一关键字递减,\(w\)为第二关键字递增排序,就有
\[ f[i]=\min_{j=1}^{i-1}\{f[j]+cost[j+1][i]\} \]
其中
\[ cost[i][j]=h[i]*\max_{k=i}^j\{w[k]\} \]
状态是\(O(n)\)的,转移可以用ST表优化到\(O(n)\)

这时候我们就得到了一个\(O(n^2)\)的优秀TLE解法

实际上这里不需要ST表,对于一块土地,如果有另一块土地,比它长,还比它宽,那它就可以在买那个土地时顺便买走

所以,最终的土地序列是\(h\)递减,\(w\)递增的

因此可以改写一下DP式
\[ f[i]=\min_{j=1}^{i-1}\{f[j]+h[j+1]\times w[i]\} \]
\(cost[i][j]=w[i]\times h[j+1]\)
\[ \begin{align} cost[i][j]+cost[i+1][j+1]=w[i]\times h[j+1]+w[i+1]\times h[j+2]\\ cost[i+1][j]+cost[i][j+1]=w[i+1]\times h[j+1]+w[i]\times h[j+2] \end{align} \]

(1)式减(2)式得
\[ LHS=cost[i][j]+cost[i+1][j+1]-cost[i+1][j]-cost[i][j+1] \]

\[ \begin{align*} RHS&=w[i]\times (h[j+1]-h[j+2])+w[i+1]\times(h[j+2]-h[j+1])\\ &=(h[j+1]-h[j+2])\times (w[i]-w[i+1]) \end{align*} \]

由于h递减,w递增
\[ \begin{align*} h[j+1]-h[j+2]>0\\ w[i]-w[i+1]<0 \end{align*} \]
因此
\[ RHS<0 \]
所以
\[ cost[i][j]+cost[i+1][j+1]<cost[i][j+1]+cost[i+1][j] \]
至此,我们严格证明了\(cost\)是一个凸函数,所以该DP式满足决策单调性

我们可以用单调队列维护,复杂度\(O(nlogn)\)

(不知道为什么甚至用了deque,跑得比大部分\(O(n)\)的斜率优化还快)

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<bitset>
#include<deque>
using namespace std;

inline int rd(){
  int ret=0,f=1;char c;
  while(c=getchar(),!isdigit(c))f=c=='-'?-1:1;
  while(isdigit(c))ret=ret*10+c-'0',c=getchar();
  return ret*f;
}

const int MAXN = 50005;

inline void upmax(int &x,int y){x=max(x,y);}
inline void upmin(int &x,int y){x=min(x,y);}

int n;
typedef long long ll;

struct Node{
  int x,y;
  bool operator <(const Node &rhs)const{
    return x==rhs.x?y>rhs.y:x>rhs.x;
  }
}tmp[MAXN],node[MAXN];
int tot;

struct Que{
  int l,r,p;
  Que(int _p,int _l,int _r){l=_l;r=_r;p=_p;}
};

deque<Que> Q;
ll f[MAXN];

ll calc(int p,int x){
  return f[p]+1ll*node[p+1].x*node[x].y;
}

int fnd(int x){
  int l=Q.back().l,r=Q.back().r,p=Q.back().p,ret;
  while(l<r){
    int mid=(l+r)>>1;
    if(calc(x,mid)<=calc(p,mid))r=mid;
    else ret=mid,l=mid+1;
  }
  return ret+1;
}

signed main(){
  memset(f,0x7f,sizeof(f));
  f[0]=0;
  n=rd();
  for(int i=1;i<=n;i++)tmp[i].x=rd(),tmp[i].y=rd();
  sort(tmp+1,tmp+1+n);
  for(int i=1;i<=n;i++){
    if(tot&&node[tot].y>=tmp[i].y)continue;
    node[++tot]=tmp[i];
  }
  Q.push_back(Que(0,1,tot));
  for(int i=1;i<=tot;i++){
    while(!Q.empty()&&Q.front().r<i)Q.pop_front();
    f[i]=calc(Q.front().p,i);//f[i]=f[p]+cost[p+1][i]
    if(calc(i,tot)>calc(Q.back().p,tot))continue;
    while(!Q.empty()&&calc(i,Q.back().l)<calc(Q.back().p,Q.back().l))Q.pop_back();
    if(Q.empty()){Q.push_back(Que(i,i+1,tot));continue;}
    int pos=fnd(i);
    Q.back().r=pos-1;Q.push_back(Que(i,pos,tot));
  }
  cout<<f[tot];
  return 0;
}

转载于:https://www.cnblogs.com/ghostcai/p/9811193.html

1. 内容概要 本项目是一个支持科学函数的命令行计算器,兼容 C++98 标准。它实现了中缀表达式词法分析、后缀表达式转换与求值,支持常见数学运算(如幂、三角函数、对数等)与括号优先级解析。程序还提供了角度版三角函数、角度与弧度互转功能,并支持函数调试输出与函数演示模式。 2. 适用人群 * C++ 初中级学习者,特别是希望深入理解表达式求值机制者 * 需要一个可扩展的计算引擎的项目开发者 * 想通过项目实践词法分析、调度场算法、数学函数封装的开发者 * 高校学生课程设计、编译原理实践者 3. 使用场景及目标 * 实现中缀表达式的完整求解器,支持函数嵌套、优先级与结合性处理 * 提供角度与弧度版本的三角函数,以适应不同输入偏好 * 演示中缀转后缀过程,辅助编程教育与算法教学 * 提供科学函数辅助计算,如 `log`, `sqrt`, `abs`, `exp`, `ceil`, `floor` 等 4. 其他说明 * 支持函数:sin, cos, tan(弧度);sind, cosd, tand(角度) * 支持函数嵌套,如 `sin(deg2rad(30))` * 支持操作符:+, -, \*, /, ^, \*\*(幂运算)与括号优先级 * 所有函数均通过 map 注册,方便扩展与自定义 * 输入 `help` 查看支持函数,`demo` 观看转后缀过程,`quit` 退出程序 * 提示用户避免使用 `°` 符号,推荐使用角度函数代替 * 可通过 `g++ calculator.cpp -o calculator -lm` 编译(需链接数学库)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值