传送门
- 思路:预处理
,每一个
是
单增
单减的,那么考虑
之间的转移
我们考虑两个决策点,其中
为最优决策点,那么
注意到这是个斜率为正的斜线,而当前转移的又是类似反比例形状的,那么最优决策点一定会将转移点分成两部分,那么这个就可以通过分治来解决了
下面来考虑的限制,发现对应的是一段区间,那么我们线段树分治,每个结点做一次决策单调性
即可,
#include<bits/stdc++.h>
#define cs const
#define fi first
#define se second
#define pb push_back
using namespace std;
namespace IO{
cs int Rlen=1<<22|1;
inline char gc(){
static char buf[Rlen],*p1,*p2;
(p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin));
return p1==p2?EOF:*p1++;
}
int read(){
int x=0; char c=gc(); bool f=false;
while(!isdigit(c)) f=(c=='-'),c=gc();
while(isdigit(c)) x=(((x<<2)+x)<<1)+(c^48), c=gc();
return f?-x:x;
}
} using namespace IO;
cs int N = 1e6 + 50;
typedef long long ll;
cs ll INF = 1e16;
typedef pair<int, int> pi;
int n, m; pi a[N]; ll f[N];
vector<int> S[N]; int c[N];
void add(int x, int v){ assert(x<=m); for(;x<=m;x+=x&-x) c[x]=max(c[x],v); }
int ask(int x){ int as=0; for(;x;x-=x&-x) as=max(as,c[x]); return as; }
namespace cdq{
vector<int> A, B;
void work(int l, int r, int L, int R){
if(l>r) return;
int mid=(l+r)>>1, ps=A[mid], trs=0; ll now=INF;
for(int i=L; i<=R; i++){
assert(a[ps].fi > a[B[i]].fi);
assert(a[ps].se > a[B[i]].se);
ll x = a[ps].se-a[B[i]].se, y = a[ps].fi-a[B[i]].fi;
if(f[B[i]] + x * y < now) now = f[B[i]] + x * y, trs=i;
} f[ps]=min(f[ps],now);
work(l,mid-1,trs,R); work(mid+1,r,L,trs);
}
}
namespace SGT{
cs int N = ::N << 2;
vector<int> S[N];
#define mid ((l+r)>>1)
vector<int> now;
void clr(int x, int l, int r){
S[x].clear(); if(l==r) return;
clr(x<<1,l,mid); clr(x<<1|1,mid+1,r);
}
void ins(int x, int l, int r, int c){
if(a[now[l]].se <= a[c].se && a[now[r]].fi <= a[c].fi){
S[x].pb(c); return;
} if(a[now[r]].se >= a[c].se) return;
if(a[now[l]].fi >= a[c].fi) return;
ins(x<<1,l,mid,c); ins(x<<1|1,mid+1,r,c);
}
void work(int x, int l, int r){
if(S[x].size()) cdq::A=S[x], cdq::work(0,S[x].size()-1,l,r);
if(l==r) return; work(x<<1,l,mid); work(x<<1|1,mid+1,r);
}
#undef mid
}
int main(){
#ifdef FSYolanda
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif
n=read(), m=read();
for(int i=1; i<=n; i++) a[i].fi=read(), a[i].se=read();
a[++n]=pi(m,m); sort(a+1,a+n+1); int MAX=0;
for(int i=1,x; i<=n; i++)
x=ask(a[i].se-1)+1, MAX=max(MAX,x), add(a[i].se,x), S[x].pb(i);
memset(f,0x3f,sizeof(f)); f[0]=0;
for(int c : S[1]) f[c] = (ll)a[c].fi * a[c].se;
for(int i=2; i<=MAX; i++){
SGT::clr(1,0,S[i-1].size()-1); SGT::now=cdq::B=S[i-1];
for(int c : S[i]) SGT::ins(1,0,S[i-1].size()-1,c);
SGT::work(1,0,S[i-1].size()-1);
} cout<<f[n]; return 0;
}
}