思路:预处理
l
i
s
lis
lis,每一个
l
i
s
lis
lis 是
x
x
x 单增
y
y
y 单减的,那么考虑
l
i
s
lis
lis 之间的转移 我们考虑两个决策点
j
,
k
j,k
j,k,其中
j
j
j 为最优决策点,那么
d
p
j
+
x
j
y
j
−
x
j
y
i
−
y
j
x
i
≤
d
p
k
+
x
k
y
k
−
x
k
y
i
−
y
k
x
i
(
y
k
−
y
j
)
x
i
+
(
x
k
−
x
j
)
y
i
≤
f
k
−
f
j
dp_j+x_jy_j-x_jy_i-y_jx_i\le dp_k+x_ky_k-x_ky_i-y_kx_i\\ (y_k-y_j)x_i+(x_k-x_j)y_i\le f_k-f_j
dpj+xjyj−xjyi−yjxi≤dpk+xkyk−xkyi−ykxi(yk−yj)xi+(xk−xj)yi≤fk−fj 注意到这是个斜率为正的斜线,而当前转移的
l
i
s
lis
lis 又是类似反比例形状的,那么最优决策点一定会将转移点分成两部分,那么这个就可以通过分治来解决了 下面来考虑
x
j
≤
x
i
,
y
j
≤
y
i
x_j\le x_i,y_j\le y_i
xj≤xi,yj≤yi 的限制,发现对应的是一段区间,那么我们线段树分治,每个结点做一次决策单调性
d
p
dp
dp 即可,
O
(
n
log
2
n
)
O(n\log^2 n)
O(nlog2n)
#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++;}intread(){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);}intask(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
}intmain(){
#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];return0;}}