【省选模拟】20/04/13 切割 (分治)(决策单调性)(DP)

传送门

  • 思路:预处理 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+xjyjxjyiyjxidpk+xkykxkyiykxi(ykyj)xi+(xkxj)yifkfj
    注意到这是个斜率为正的斜线,而当前转移的 l i s lis lis 又是类似反比例形状的,那么最优决策点一定会将转移点分成两部分,那么这个就可以通过分治来解决了
    下面来考虑 x j ≤ x i , y j ≤ y i x_j\le x_i,y_j\le y_i xjxi,yjyi 的限制,发现对应的是一段区间,那么我们线段树分治,每个结点做一次决策单调性 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++;
	}
	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;
}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值