zoukankan      html  css  js  c++  java
  • 「题解」USACO15FEB Fencing the Herd G

    本文将同步发布于:

    题目

    题目链接:洛谷 P3122USACO 官网

    题意概述

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

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

    初始时有 (nleq 10^5) 个点,共有 (qleq 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 pin S,operatorname{sgn}(ax_p+by_p-c)=operatorname{sgn}(ax_q+by_q-c) ]

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

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

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

    [max_{pin S}{ax_p+by_p-c} ]

    [min_{pin S}{ax_p+by_p-c} ]

    利用几何性质解决问题

    先来求解最大值吧。

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

    先将直线转化为斜截式:

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

    我们发现:

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

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

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

    先将直线转化为斜截式:

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

    我们发现:

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

    分治方法优化转移

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

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

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

    维护凸包所需时间复杂度为排序的 (Theta(nlog_2n)),结合主定理分析可知最终时间复杂度为 (Theta(nlog_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('
    ');
    	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;
    }
    
  • 相关阅读:
    flask 文件上传(单文件上传、多文件上传)--
    flask 自定义验证器(行内验证器、全局验证器) --
    flask 使用宏渲染表单(包含错误信息) --
    flask 在模板中渲染错误消息 --
    flask 在视图函数中验证表单 --
    flask 处理表单数据 --
    flask 在模板中渲染表单 --
    flask 使用Flask-WTF处理表单 --
    flask 表单
    iPad适合写作吗
  • 原文地址:https://www.cnblogs.com/Lu-Anlai/p/14821992.html
Copyright © 2011-2022 走看看