[USACO19FEB]Mowing Mischief

题目大意:

给定平面上的一些点,求这些点的一个\(LIS\),并且还需要满足下列式子最小:

\[\sum_{i=1}^{n-1}(a[i+1].x-a[i].x)*(a[i+1].y-a[i].y) \]

题解:

比较巧妙的一道题。

首先我们需要找出一个性质,我们先令\(dp[i]\)表示以\(i\)点结尾的\(LIS\),然后这些\(LIS\)相同的点在平面上是横坐标递增,纵坐标递减的,下面我们说的转移点的顺序都是按照这个顺序来的。

然后我们在观察转移,我们令两个转移点\(j\)\(k\),若\(k\)\(j\)更优,那么有:

\[dp[k]+(a[i].x-a[k].x)*(a[i].y-a[k].y)\geq dp[j]+(a[i].x-a[j].x)*(a[i].y-a[j].y) \]

\[a[i].x*(a[j].y-a[k].y)+a[i].y*(a[j].x-a[k].x)\geq dp[j]-dp[k]+a[j].x*a[j].y-a[k].x*a[k].y \]

\[A*a[i].x+B*a[i].y\geq C \]

可以看出,这其实是一个半平面,结合上面的性质,对于一排待转移点,更优的转移是一段前缀或者一段后缀,这启发我们这道题中有决策单调性。

但是这个东西还有一个条件就是\(a[i].x\geq a[j].x\ \ a[i].y\geq a[j].y\),这个东西其实我们发现合法的转移点也是一段连续的区间,这启发我们在外面线段树分治解决这个限制。

对于决策单调性的部分,我们可以令\(k\)\(j\)的后面一个点,那么上面的\(A\)是负的\(B\)是正的,所以合法的区域在直线上方,按照这个做决策单调性就好了。

代码

#include<bits/stdc++.h>
#define M 1000009
#define N 200009
using namespace std;
typedef long long ll;
vector<int>vec[N],now;
vector<int>::iterator it;
int n,T,dp[N];
ll f[N],ans;
inline ll rd(){
	ll x=0;char c=getchar();bool f=0;
	while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f?-x:x;
}
struct BIT{
	int tr[M];
	inline void add(int x,int y){
		while(x<=T)tr[x]=max(tr[x],y),x+=x&-x;
	}
	inline int query(int x){
	    int ans=0;
	    while(x)ans=max(ans,tr[x]),x-=x&-x;
	    return ans;
	}
}T1;
struct point{
	int x,y;
	inline bool operator <(const point &b)const{
		if(x!=b.x)return x<b.x;
		else return y<b.y;
	}
}a[N];
struct seg{
	#define ls tr[cnt].l
	#define rs tr[cnt].r
	int rot,tott;
	struct node{
		int l,r;
		vector<int>nw;
    }tr[N<<1];
    void build(int &cnt,int l,int r){
    	cnt=++tott;tr[cnt].l=tr[cnt].r=0;
    	tr[cnt].nw.clear();
    	if(l==r)return;
    	int mid=(l+r)>>1; 
    	build(ls,l,mid);build(rs,mid+1,r);
	}
    inline void init(){
    	tott=0;
    	build(rot,0,now.size()-1);
	}
	inline void upd(int cnt,int l,int r,int id){
		if(a[id].x>=a[now[r]].x&&a[id].y>=a[now[l]].y){
			tr[cnt].nw.push_back(id);
			return;
		}
		if(a[id].x<a[now[l]].x||a[id].y<a[now[r]].y)return;
		if(l==r)return;
		int mid=(l+r)>>1;
		upd(ls,l,mid,id);upd(rs,mid+1,r,id);
	}
	inline void _upd(int tag,int l,int r,int L,int R){
		if(l>r)return; 
		int no=tr[tag].nw[(l+r)>>1];
		ll biu=1e18,tg=0;
		for(int i=L;i<=R;++i){
			int id=now[i];
			ll x=f[id]+1ll*(a[no].x-a[id].x)*(a[no].y-a[id].y);
			if(x<biu){
				biu=x;
				tg=i;
			}
		}
		f[no]=min(f[no],biu);
		int mid=(l+r)>>1;
		_upd(tag,l,mid-1,tg,R);
		_upd(tag,mid+1,r,L,tg);
	} 
	inline void work(int id){
		upd(rot,0,now.size()-1,id);
	}
	void solve(int cnt,int l,int r){
		_upd(cnt,0,tr[cnt].nw.size()-1,l,r);
		if(l==r)return;
		int mid=(l+r)>>1;
		solve(ls,l,mid);solve(rs,mid+1,r);
	}
	inline void solve(){
		solve(rot,0,now.size()-1);
	}
	#undef ls
	#undef rs
}T2;
int main(){
	n=rd();T=rd();
	for(int i=1;i<=n;++i){
		a[i].x=rd();a[i].y=rd();
	}
	sort(a+1,a+n+1);
	int maxx=0;
	for(int i=1;i<=n;++i){
		dp[i]=T1.query(a[i].y)+1;
		T1.add(a[i].y,dp[i]);
		vec[dp[i]].push_back(i);
		maxx=max(maxx,dp[i]);
	}
	memset(f,0x3f,sizeof(f));
	for(it=vec[1].begin();it!=vec[1].end();++it){
		int x=*it;
		f[x]=1ll*a[x].x*a[x].y;
	}
	for(int i=2;i<=maxx;++i){
		now=vec[i-1];
		T2.init();
		for(it=vec[i].begin();it!=vec[i].end();++it){
			int x=*it;
			T2.work(x);
		}
		T2.solve();
	}
	ans=1e18;
	for(it=vec[maxx].begin();it!=vec[maxx].end();++it){
		int x=*it;
		ans=min(ans,f[x]+1ll*(T-a[x].x)*(T-a[x].y));
	}
	cout<<ans; 
	return 0;
}
posted @ 2019-04-29 09:10  comld  阅读(642)  评论(0编辑  收藏  举报