zoukankan      html  css  js  c++  java
  • 【洛谷5244】[USACO19FEB] Mowing Mischief P(线段树分治+决策单调性)

    点此看题面

    • 一张(T imes T)的网格图,给定(n)个关键点,可以从中选出若干个,满足每个点都在前一个点的右上方。
    • 要求在选出点数尽可能多的前提下,求出相邻两关键点(包括第一个关键点与((0,0)),最后一个关键点与((T,T)))所夹矩形面积之和的最小值。
    • (nle2 imes10^5,Tle10^6),保证每行每列最多只有一个关键点

    (LIS)分层

    首先按照横坐标排序,那么我们能选出的点的纵坐标形成一个上升子序列。

    现在要求选出点数尽可能多,就是要求最长上升子序列。

    我们记(f_i)表示以(i)为结尾的最长上升子序列长度,则根据(LIS)问题的经典理论,我们必须要对于每个(f_i)选出恰好一个点(w_{f_i}),满足(w_{f_i})(w_{f_i-1})的右上方。

    因此对于这道题我们可以分层转移。

    线段树分治

    考虑(f_i)相同的若干个点,由于我们之前已经按横坐标排过序了,因此它们的纵坐标肯定递减(否则,它们之间就存在转移关系,(f_i)不可能相同)。

    而对于(p_i),上一层的一个点(p_j)能转移到(p_i),需要满足(p_j)的两维坐标都小于(p_i),因此转移范围应该是上一层的所有点中的一段区间。

    于是我们利用线段树分治,把(p_i)扔到能转移到它的区间在线段树中对应的节点上,那么这个限制就被化掉了。

    这样一来,所有上一层的转移点都能自由地转移到这一层的所有点,问题就简化了许多。

    决策单调性

    比较两个转移点(p_j,p_k(j<k)),判断何时(j)优于(k)

    [f_j+(x_i-x_j)(y_i-y_j)<f_k+(x_i-x_k)(y_i-y_k)\ y_i(x_k-x_j)+x_i(y_k-y_j)<f_k+x_ky_k-f_j-x_jy_j ]

    (y_i,x_i)看成变量,也就是说:

    [y_i<frac{y_j-y_k}{x_k-x_j}x_i+frac{f_k+x_ky_k-f_j-x_jy_j}{x_k-x_j} ]

    即,使得(j)优于(k)((x_i,y_i))位于一条斜率为正的直线右下方的一个半平面,具有决策单调性。

    但要注意,这里的决策单调性是这一层越靠后的位置,越可能选择上一层靠前的位置

    代码:(O(nlog^2n))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 200000
    #define M 1000000
    #define LL long long
    #define INF (LL)1e18
    using namespace std;
    int n,m;struct P {int x,y;I bool operator < (Con P& o) Con {return x<o.x;}}p[N+5];
    vector<P> w[N+5];vector<LL> f[N+5];
    namespace FastIO
    {
    	#define FS 100000
    	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
    	char oc,FI[FS],*FA=FI,*FB=FI;
    	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
    }using namespace FastIO;
    namespace DP//决策单调性优化DP
    {
    	P p[N+5],q[N+5];LL f[N+5],ans[N+5];
    	I void Solve(CI l,CI r,CI L,CI R)//分治决策单调性
    	{
    		#define Calc(i) (f[i]+1LL*(q[mid].x-p[i].x)*(q[mid].y-p[i].y))
    		if(l>r) return;RI mid=l+r>>1,g=L;for(RI i=L+1;i<=R;++i) Calc(g)>Calc(i)&&(g=i);//枚举最优决策点
    		ans[mid]=Calc(g),Solve(l,mid-1,g,R),Solve(mid+1,r,L,g);//这一层越靠后的位置,越可能选择上一层靠前的位置
    	}
    }
    int sz;class SegmentTree
    {
    	private:
    		#define PT CI l=0,CI r=sz-1,CI rt=1
    		#define LT l,mid,rt<<1
    		#define RT mid+1,r,rt<<1|1
    		vector<int> V[N<<2];
    	public:
    		I void K(CI L,CI R,CI p,PT)//把区间扔到线段树上
    		{
    			if(L<=l&&r<=R) return (void)V[rt].push_back(p);
    			RI mid=l+r>>1;L<=mid&&(K(L,R,p,LT),0),R>mid&&(K(L,R,p,RT),0);
    		}
    		I void Solve(CI id,PT)//线段树分治
    		{
    			if(!V[rt].empty())
    			{
    				RI i,sz;for(i=l;i<=r;++i) DP::p[i-l+1]=w[id-1][i],DP::f[i-l+1]=f[id-1][i];
    				for(sz=V[rt].size(),i=0;i^sz;++i) DP::q[i+1]=w[id][V[rt][i]];
    				for(DP::Solve(1,sz,1,r-l+1),i=0;i^sz;++i) f[id][V[rt][i]]=min(f[id][V[rt][i]],DP::ans[i+1]);V[rt].clear();//与原有答案比较
    			}
    			if(l==r) return;RI mid=l+r>>1;Solve(id,LT),Solve(id,RT);
    		}
    }S;
    struct TreeArray
    {
    	int a[M+5];I void U(RI x,CI v) {W(x<=m&&a[x]<v) a[x]=v,x+=x&-x;}
    	I int Q(RI x) {RI t=0;W(x) t=max(t,a[x]),x-=x&-x;return t;}
    }T;
    I int FX(Con vector<P>& v,CI x) {RI l=0,r=v.size()-1,mid;W(l^r) v[mid=l+r+1>>1].x<x?l=mid:r=mid-1;return l;}//二分最后的横坐标小于x的位置
    I int FY(Con vector<P>& v,CI y) {RI l=0,r=v.size()-1,mid;W(l^r) v[mid=l+r-1>>1].y<y?r=mid:l=mid+1;return r;}//二分第一个横坐标小于y的位置
    int main()
    {
    	RI i,j,x;for(read(n),read(m),i=1;i<=n;++i) read(p[i].x),read(p[i].y);
    	RI Mx=0;for(sort(p+1,p+n+1),i=1;i<=n;++i) T.U(p[i].y,x=T.Q(p[i].y)+1),w[x].push_back(p[i]),Mx=max(Mx,x);//按横坐标排序,树状数组求LIS并分层
    	for(w[0].push_back((P){0,0}),f[0].push_back(0),i=1;i<=Mx;S.Solve(i++))//分层转移
    		for(sz=w[i-1].size(),x=w[i].size(),j=0;j^x;++j) S.K(FY(w[i-1],w[i][j].y),FX(w[i-1],w[i][j].x),j),f[i].push_back(INF);//求出转移范围,利用线段树分治化掉转移限制
    	LL t=INF;for(j=0;j^x;++j) t=min(t,f[Mx][j]+1LL*(m-w[Mx][j].x)*(m-w[Mx][j].y));return printf("%lld
    ",t),0;//枚举最后一层点求答案
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    指向老域名的反链丢失问题
    oracle express介绍
    VB6:通过ADO访问Oracle存储过程返回的结果集
    Oracle学习笔记:理解oracle的编程接口oo4o的对象模型
    Oracle学习笔记:oracle的编程接口
    VB6:编写一个分析sqlserver存储过程执行语句"execute procedurename par1,par2,......."语法是否正确的函数
    惊喜!使用Regcure修复注册表错误,Oracle客户端可以使用了
    最新30佳精美的名片设计作品欣赏
    28个经过重新设计的著名博客案例
    向设计师推荐20款漂亮的免费英文字体
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu5244.html
Copyright © 2011-2022 走看看