【洛谷 P5244】 [USACO19FEB]Mowing Mischief P(分治 / 决策单调性优化DP)

传送门

首先求出 l i s lis lis
然后对于 l i s i lis_i lisi按照值分层转移
显然每一层的点是从左上到右下这样单调的

对于相邻两层的转移
可以列出 D P DP DP式子
f i = min ⁡ j , x j < x i , y j < y i ( f j + ( x i − x j ) ∗ ( y i − y j ) ) f_i=\min_{j,x_j<x_i,y_j<y_i}(f_j+(x_i-x_j)*(y_i-y_j)) fi=minj,xj<xi,yj<yi(fj+(xixj)(yiyj))
冷静一下发现没法直接斜率优化

首先不考虑 i → j i\rightarrow j ij要满足 x i < x j , y i < y j x_i<x_j,y_i<y_j xi<xj,yi<yj的限制
考虑如果 i , j ( i < j ) i,j(i<j) i,j(i<j)转移到 k k k
j j j i i i
f j + ( x k − x j ) ( y k − y j ) < f i + ( x k − x i ) ( y k − y i ) f_j+(x_k-x_j)(y_k-y_j)<f_i+(x_k-x_i)(y_k-y_i) fj+(xkxj)(ykyj)<fi+(xkxi)(ykyi)
x k ( y i − y j ) + y k ( x i − x j ) ≤ ( f i + x i y i ) − ( f j + x j y j ) x_k(y_i-y_j)+y_k(x_i-x_j)\le(f_i+x_iy_i)-(f_j+x_jy_j) xk(yiyj)+yk(xixj)(fi+xiyi)(fj+xjyj)
由于 i , j i,j i,j同一层是单调的,所以 y i − y j , x i − x j y_i-y_j,x_i-x_j yiyj,xixj一定一正一负
对于 i , j i,j i,j看做常量的话就是对于 k ( x k , y k ) k(x_k,y_k) k(xk,yk)就是一个斜率为正的直线的一侧
由于 k k k也是单调的
所以转移具有决策单调性
不过注意如果按照 x x x有序的话决策单调性是反的
i , j i,j i,j越大,可以转移的 k k k为越小的一段
于是分治优化即可

现在考虑 x , y x,y x,y大小的限制
可以分治优化
由于每一层点单调
所以分治可以做到一个 l o g log log

复杂度 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)

#include<bits/stdc++.h>
using namespace std;
#define cs const
#define int long long
#define re register
#define pb push_back
#define pii pair<int,int>
#define ll long long
#define y1 shinkle
#define fi first
#define se second
#define bg begin
cs int RLEN=1<<20|1;
inline char gc(){
    static char ibuf[RLEN],*ib,*ob;
    (ib==ob)&&(ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
    return (ib==ob)?EOF:*ib++;
}
inline int read(){
    char ch=gc();
    int res=0;bool f=1;
    while(!isdigit(ch))f^=ch=='-',ch=gc();
    while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=gc();
    return f?res:-res;
}
inline ll readll(){
    char ch=gc();
    ll res=0;bool f=1;
    while(!isdigit(ch))f^=ch=='-',ch=gc();
    while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=gc();
    return f?res:-res;
}
inline int readstring(char *s){
	int top=0;char ch=gc();
	while(isspace(ch))ch=gc();
	while(!isspace(ch)&&ch!=EOF)s[++top]=ch,ch=gc();
	return top;
}
template<typename tp>inline void chemx(tp &a,tp b){a<b?a=b:0;}
template<typename tp>inline void chemn(tp &a,tp b){a>b?a=b:0;}
cs int N=200005,M=1000005;
int n,lm,ln;
namespace bit{
	int tr[M];
	#define lb(x) (x&(-x))
	inline void update(int p,int k){
		for(;p<=lm;p+=lb(p))chemx(tr[p],k);
	}
	inline int query(int p,int res=0){
		for(;p>0;p-=lb(p))chemx(res,tr[p]);return res;
	}
}
pii p[N];
inline ll dis(pii x,pii y){
	return abs((ll)(x.fi-y.fi)*(x.se-y.se));
}
int ls[N];
ll f[N];
vector<int> pt[N],z;
namespace Seg{
	vector<int> q[N<<2],tp;
	#define lc (u<<1)
	#define rc ((u<<1)|1)
	#define mid ((l+r)>>1)
	void update(int u,int l,int r,int k){
	//	cout<<z[l]<<" "<<z[r]<<" "<<k<<" "<<p[k].se<<" "<<p[z[l]].se<<" "<<p[k].fi<<" "<<p[z[r]].fi<<'\n';
		if(p[k].se>=p[z[l]].se&&p[k].fi>=p[z[r]].fi){
		//	cout<<l<<" "<<r<<" "<<k<<'\n';
			q[u].pb(k);return;
		}
		if(p[k].se<=p[z[r]].se||p[k].fi<=p[z[l]].fi)return;
		update(lc,l,mid,k),update(rc,mid+1,r,k);
	}
	void solve(int l,int r,int ql,int qr){
		ll res=1e18;int ps=0,id=tp[mid];
		for(int i=ql;i<=qr;i++){
			ll now=f[z[i]]+dis(p[z[i]],p[id]);
			if(now<res)res=now,ps=i;
		}
		chemn(f[id],res);
		if(l<mid)solve(l,mid-1,ps,qr);
		if(mid<r)solve(mid+1,r,ql,ps);
	}
	void query(int u,int l,int r){
		if(q[u].size()){
			tp=q[u];
			solve(0,q[u].size()-1,l,r);
			q[u].clear();
		}
		if(l==r)return;
		query(lc,l,mid),query(rc,mid+1,r);
	}
	#undef lc
	#undef rc
	#undef mid
}
inline void trans(cs vector<int> &pre,cs vector<int> &now){
	z=pre;
	int l=(int)pre.size()-1;
	for(int x:now){
		Seg::update(1,0,l,x);
	}
	Seg::query(1,0,l);
}
signed main(){
	#ifdef Stargazer
	freopen("lx.in","r",stdin);
	#endif
	n=read(),lm=read();
	for(int i=1;i<=n;i++)p[i].fi=read(),p[i].se=read();
	sort(p+1,p+n+1);
	for(int i=1;i<=n;i++){
		ls[i]=bit::query(p[i].se)+1;
		chemx(ln,ls[i]);
		bit::update(p[i].se,ls[i]);
		pt[ls[i]].pb(i);
	//	cout<<i<<" "<<ls[i]<<'\n';
	}
	memset(f,127/3,sizeof(f));
	for(int &i:pt[1])f[i]=dis(p[i],pii(0,0));
	//,cout<<i<<" "<<f[i]<<'\n';
	for(int i=2;i<=ln;i++)
	trans(pt[i-1],pt[i]);
	ll res=1e18;
	for(int &i:pt[ln])chemn(res,f[i]+dis(p[i],pii(lm,lm)));
	cout<<res<<'\n';
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值