「题解」USACO15FEB Fencing the Herd G

Posted Lu_Anlai

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了「题解」USACO15FEB Fencing the Herd G相关的知识,希望对你有一定的参考价值。

本文将同步发布于:

题目

题目链接:洛谷 P3122USACO 官网

题意概述

给你平面上的一些点和直线,有两种操作:

  • 新加入一个点 \\((x,y)\\)
  • 给定一条直线 \\(ax+by=c\\),询问是否所有点都在这条直线的同侧(在直线上不合法)。

初始时有 \\(n\\leq 10^5\\) 个点,共有 \\(q\\leq 10^5\\) 次操作。

题解

对题意转化

我们考虑将 所有点都在直线的同一侧 这一条件进行转化。

具体地,我们先考虑一个简单的子问题。

简单子问题

问题是这样的,对于一条标准形式的直线 \\(ax+by+c=0\\),在它同侧的点 \\((x_p,y_p)\\) 满足什么性质?

我们倒推并分类讨论:

  • \\(ax_p+by_p+c=0\\),显然点 \\((x_p,y_p)\\) 在这条直线上;
  • \\(ax_p+by_p+c<0\\),显然它与所有点 \\(q\\) 满足 \\(ax_q+by_q+c<0\\) 在直线的同侧;
  • \\(ax_p+by_p+c>0\\),显然它与所有点 \\(q\\) 满足 \\(ax_q+by_q+c>0\\) 在直线的同侧;

因此我们发现,如果两个点在一条直线的同侧,则将两点坐标代入直线方程得到的结果同号。


解决了子问题,我们对题意进行转化,得到如下式子。

一条直线合法当且仅当

\\[\\forall p\\in S,\\operatorname{sgn}(ax_p+by_p-c)=\\operatorname{sgn}(ax_q+by_q-c) \\]

意思就是所有点对应的符号相同(暂时不考虑点在直线上)。

进一步地,一堆数同号说明它们的最大值与最小值同号。

问题转变成为,给定一条直线 \\(ax+by-c=0\\),求

\\[\\max_{p\\in S}\\{ax_p+by_p-c\\} \\]

\\[\\min_{p\\in S}\\{ax_p+by_p-c\\} \\]

利用几何性质解决问题

先来求解最大值吧。

设直线为 \\(ax+by-c=\\texttt{max}\\)

先将直线转化为斜截式:

\\[y=-\\frac{a}{b}x+\\frac{c+\\texttt{sum}}{b} \\]

我们发现:

  • \\(b>0\\),我们只需要最大化该直线在 \\(y\\) 轴上的截距即可,维护一个上凸包,在上凸包上二分斜率求解即可;
  • \\(b<0\\),我们则可以通过变号等方法修改成为 \\(b>0\\) 的情况;
  • \\(b=0\\),我们发现此时的最大值必定在凸包的右端点取到,若用 \\(\\texttt{max}\\) 值作为判据,则不受影响,无需考虑。

再来求解最小值,还是一样的步骤:

设直线为 \\(ax+by-c=\\texttt{min}\\)

先将直线转化为斜截式:

\\[y=-\\frac{a}{b}x+\\frac{c+\\texttt{min}}{b} \\]

我们发现:

  • \\(b>0\\),我们只需要最小化该直线在 \\(y\\) 轴上的截距即可,维护一个下凸包,在下凸包上二分斜率求解即可;
  • \\(b<0\\),我们则可以通过变号等方法修改成为 \\(b>0\\) 的情况;
  • \\(b=0\\),我们发现此时的最小值必定在凸包的右端点取到,若用 \\(\\texttt{min}\\) 值作为判据,则不受影响,无需考虑。

分治方法优化转移

上述方法需要动态维护两个凸包,并且凸包的横坐标不具有单调性,这需要用 平衡树 来维护。

考虑到本题并不强制在线,我们考虑离线下来,利用 cdq 分治来维护凸包,时间复杂度为 \\(\\Theta(n\\log^2n)\\)(视作 \\(n,q\\) 同阶)。

具体地,我们考虑对点和直线的加入时间进行分治,假设当前要处理区间 \\([l,r]\\) 内的事件。我们就只要考虑 区间 \\([l,\\texttt{mid}]\\) 内的点形成的凸包对 区间 \\([\\texttt{mid}+1,r]\\) 内直线的贡献即可。

维护凸包所需时间复杂度为排序的 \\(\\Theta(n\\log_2n)\\),结合主定理分析可知最终时间复杂度为 \\(\\Theta(n\\log_2^2n)\\)

参考程序

更多细节参见参考程序。

#include<bits/stdc++.h>
using namespace std;
#define reg register
typedef long long ll;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
static char buf[1<<21],*p1=buf,*p2=buf;
#define flush() (fwrite(wbuf,1,wp1,stdout),wp1=0)
#define putchar(c) (wp1==wp2&&(flush(),0),wbuf[wp1++]=c)
static char wbuf[1<<21];int wp1;const int wp2=1<<21;
inline int read(void){
	reg bool f=false;
	reg char ch=getchar();
	reg int res=0;
	while(!isdigit(ch))f|=(ch==\'-\'),ch=getchar();
	while(isdigit(ch))res=10*res+(ch^\'0\'),ch=getchar();
	return f?-res:res;
}

inline ll readll(void){
	reg bool f=false;
	reg char ch=getchar();
	reg ll res=0;
	while(!isdigit(ch))f|=(ch==\'-\'),ch=getchar();
	while(isdigit(ch))res=10*res+(ch^\'0\'),ch=getchar();
	return f?-res:res;
}

inline void writeln(const char* s){
	while(*s) putchar(*(s++));
	putchar(\'\\n\');
	return;
}

inline ll max(reg ll a,reg ll b){
	return a>b?a:b;
}

inline ll min(reg ll a,reg ll b){
	return a<b?a:b;
}

struct Vector{
	int x,y;
	inline Vector(reg int x=0,reg int y=0):x(x),y(y){
		return;
	}
	inline Vector operator+(const Vector& a)const{
		return Vector(x+a.x,y+a.y);
	}
	inline Vector operator-(const Vector& a)const{
		return Vector(x-a.x,y-a.y);
	}
};

inline ll dot(const Vector& a,const Vector& b){
	return 1ll*a.x*b.x+1ll*a.y*b.y;
}

inline ll cross(const Vector& a,const Vector& b){
	return 1ll*a.x*b.y-1ll*a.y*b.x;
}

typedef Vector Point;

inline bool operator<(const Point& a,const Point& b){
	return a.x<b.x||(a.x==b.x&&a.y<b.y);
}

const int MAXN=1e5+5;
const int MAXQ=1e5+5;
const ll inf=5e18;

struct updates{
	int tim;
	Point p;
};

struct querys{
	int tim;
	int A,B;
	ll C;
	ll Max,Min;
};

int n,q;
int totu,totq;
updates up[MAXN+MAXQ];
querys qu[MAXQ];

inline ll getVal(const querys& q,const Point& p){
	return 1ll*q.A*p.x+1ll*q.B*p.y-q.C;
}

inline void solve(reg int l,reg int r,reg int lu,reg int ru,reg int lq,reg int rq){
	if(l==r)
		return;
	if(lu>ru||lq>rq)
		return;
	reg int mid=(l+r)>>1;
	reg int midu,midq;
	if(up[lu].tim<=mid){
		reg int __l=lu,__r=ru,__mid;
		while(__l<__r){
			__mid=(__l+__r)>>1;
			if(up[__mid+1].tim<=mid)
				__l=__mid+1;
			else
				__r=__mid;
		}
		midu=__l;
	}
	else
		midu=lu-1;
	if(qu[lq].tim<=mid){
		reg int __l=lq,__r=rq,__mid;
		while(__l<__r){
			__mid=(__l+__r)>>1;
			if(qu[__mid+1].tim<=mid)
				__l=__mid+1;
			else
				__r=__mid;
		}
		midq=__l;
	}
	else
		midq=lq-1;
	solve(l,mid,lu,midu,lq,midq);
	if(lu<=midu&&midq+1<=rq){
		reg int tot=0;
		static Point tmp[MAXN+MAXQ];
		for(reg int i=lu;i<=midu;++i)
			tmp[++tot]=up[i].p;
		sort(tmp+1,tmp+tot+1);
		reg int top;
		static Point S[MAXN+MAXQ];
		top=0;
		for(reg int i=1;i<=tot;++i){
			while(top>1&&cross(S[top]-S[top-1],tmp[i]-S[top-1])>=0)
				--top;
			S[++top]=tmp[i];
		}
		for(reg int i=midq+1;i<=rq;++i){
			reg int __l=1,__r=top,__mid;
			while(__l<__r){
				__mid=(__l+__r)>>1;
				if(getVal(qu[i],S[__mid])<getVal(qu[i],S[__mid+1]))
					__l=__mid+1;
				else
					__r=__mid;
			}
			qu[i].Max=max(qu[i].Max,getVal(qu[i],S[__l]));
		}
		top=0;
		for(reg int i=1;i<=tot;++i){
			while(top>1&&cross(S[top]-S[top-1],tmp[i]-S[top-1])<=0)
				--top;
			S[++top]=tmp[i];
		}
		for(reg int i=midq+1;i<=rq;++i){
			reg int __l=1,__r=top,__mid;
			while(__l<__r){
				__mid=(__l+__r)>>1;
				if(getVal(qu[i],S[__mid])>getVal(qu[i],S[__mid+1]))
					__l=__mid+1;
				else
					__r=__mid;
			}
			qu[i].Min=min(qu[i].Min,getVal(qu[i],S[__l]));
		}
	}
	solve(mid+1,r,midu+1,ru,midq+1,rq);
	return;
}

int main(void){
	n=read(),q=read();
	for(reg int i=1;i<=n;++i){
		++totu;
		up[totu].tim=0,up[totu].p.x=read(),up[totu].p.y=read();
	}
	for(reg int i=1;i<=q;++i){
		static int opt;
		opt=read();
		switch(opt){
			case 1:{
				++totu;
				up[totu].tim=i,up[totu].p.x=read(),up[totu].p.y=read();
				break;
			}
			case 2:{
				++totq;
				qu[totq].tim=i,qu[totq].A=read(),qu[totq].B=read(),qu[totq].C=readll(),qu[totq].Max=-inf,qu[totq].Min=inf;
				if(qu[totq].B<0)
					qu[totq].A=-qu[totq].A,qu[totq].B=-qu[totq].B,qu[totq].C=-qu[totq].C;
				else if((!qu[totq].B)&&qu[totq].A<0)
					qu[totq].A=-qu[totq].A,qu[totq].C=-qu[totq].C;
				break;
			}
		}
	}
	solve(0,q,1,totu,1,totq);
	for(reg int i=1;i<=totq;++i)
		writeln((!qu[i].Max||!qu[i].Min||((qu[i].Max^qu[i].Min)>>63))?"NO":"YES");
	flush();
	return 0;
}

以上是关于「题解」USACO15FEB Fencing the Herd G的主要内容,如果未能解决你的问题,请参考以下文章

[USACO20FEB]Equilateral Triangles P 题解

[USACO08FEB]修路题解

题解[USACO18FEB]New Barns

题解 P3047 [USACO12FEB]附近的牛Nearby Cows

[USACO21FEB] Count the Cows G 题解

题解Luogu P2875 [USACO07FEB]牛的词汇The Cow Lexicon