「题解」USACO15FEB Fencing the Herd G
本文将同步发布于:
题目
题意概述
给你平面上的一些点和直线,有两种操作:
- 新加入一个点 \((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\) 在直线的同侧;
因此我们发现,如果两个点在一条直线的同侧,则将两点坐标代入直线方程得到的结果同号。
解决了子问题,我们对题意进行转化,得到如下式子。
一条直线合法当且仅当
\]
意思就是所有点对应的符号相同(暂时不考虑点在直线上)。
进一步地,一堆数同号说明它们的最大值与最小值同号。
问题转变成为,给定一条直线 \(ax+by-c=0\),求
\]
\]
利用几何性质解决问题
先来求解最大值吧。
设直线为 \(ax+by-c=\texttt{max}\)。
先将直线转化为斜截式:
\]
我们发现:
- 若 \(b>0\),我们只需要最大化该直线在 \(y\) 轴上的截距即可,维护一个上凸包,在上凸包上二分斜率求解即可;
- 若 \(b<0\),我们则可以通过变号等方法修改成为 \(b>0\) 的情况;
- 若 \(b=0\),我们发现此时的最大值必定在凸包的右端点取到,若用 \(\texttt{max}\) 值作为判据,则不受影响,无需考虑。
再来求解最小值,还是一样的步骤:
设直线为 \(ax+by-c=\texttt{min}\)。
先将直线转化为斜截式:
\]
我们发现:
- 若 \(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的更多相关文章
- 「题解」「美团 CodeM 资格赛」跳格子
目录 「题解」「美团 CodeM 资格赛」跳格子 题目描述 考场思路 思路分析及正解代码 「题解」「美团 CodeM 资格赛」跳格子 今天真的考自闭了... \(T1\) 花了 \(2h\) 都没有搞 ...
- 「题解」「HNOI2013」切糕
文章目录 「题解」「HNOI2013」切糕 题目描述 思路分析及代码 题目分析 题解及代码 「题解」「HNOI2013」切糕 题目描述 点这里 思路分析及代码 题目分析 这道题的题目可以说得上是史上最 ...
- 「题解」JOIOI 王国
「题解」JOIOI 王国 题目描述 考场思考 正解 题目描述 点这里 考场思考 因为时间不太够了,直接一上来就着手暴力.但是本人太菜,居然暴力爆 000 ,然后当场自闭- 一气之下,发现对 60pts ...
- 「题解」:[loj2763][JOI2013]现代豪宅
问题 A: 现代豪宅 时间限制: 1 Sec 内存限制: 256 MB 题面 题目描述 (题目译自 $JOI 2013 Final T3$「現代的な屋敷」) 你在某个很大的豪宅里迷路了.这个豪宅由东 ...
- 「题解」:$Simple$
问题 A: $Simple$ 时间限制: 1 Sec 内存限制: 256 MB 题面 题面谢绝公开. 题解 不算数学的数学题?? 直接枚举会重.$60%$两种算法:1.无脑$vis$数组记录.2.$ ...
- 「题解」CF1468M Similar Sets
本文将同步发布于: 洛谷博客: csdn: 博客园: 简书. 题目 题目链接:洛谷.CF1468M. 题意简述 给定 \(n\) 个集合 \(S_{1\sim n}\),问是否存在 \(i,j\) 满 ...
- 「题解」300iq Contest 2 H. Honorable Mention
本文将同步发布于: 洛谷博客: csdn: 博客园: 简书. 题目 题目链接:gym102331H. 题意概述 给定一个长度为 \(n\) 的序列 \(a\),有 \(q\) 次询问,每次询问给定三个 ...
- 「题解」NWRRC2017 Grand Test
本文将同步发布于: 洛谷博客: csdn: 博客园: 简书. 题目 题目链接:洛谷 P7025.gym101612G. 题意概述 给你一张有 \(n\) 个点 \(m\) 条边的无向图,无重边无自环, ...
- 「题解」:$Six$
问题 A: Six 时间限制: 1 Sec 内存限制: 512 MB 题面 题面谢绝公开. 题解 来写一篇正经的题解. 每一个数对于答案的贡献与数本身无关,只与它包含了哪几个质因数有关. 所以考虑二 ...
随机推荐
- Bugku-文件包含2
文件包含2 目录 文件包含2 题目描述 解题过程 参考 题目描述 没有描述 解题过程 文件包含题目大多都是php环境的, 所以先试试伪协议 发现php://被ban了 继续尝试,发现file://协议 ...
- web技术培训(二)-Flask后端框架初识
web网站发展至今,特别是服务器端,涉及到的知识.内容,非常广泛.这对程序员的要求会越来越高.如果采用成熟,稳健的框架,那么一些基础的工作,比如,安全性,数据流控制等都可以让框架来处理,那么程序开发人 ...
- .NET生成小程序码,并合自定义背景图生成推广小程序二维码
前言: 对于小程序大家可能都非常熟悉了,随着小程序的不断普及越来越多的公司都开始推广使用起来了.今天接到一个需求就是生成小程序码,并且于运营给的推广图片合并在一起做成一张漂亮美观的推广二维码,扫码这种 ...
- 一种巧妙的使用 CSS 制作波浪效果的思路
在之前,我介绍过几种使用纯 CSS 实现波浪效果的方式,关于它们有两篇相关的文章: 纯 CSS 实现波浪效果! 巧用 CSS 实现酷炫的充电动画 本文将会再介绍另外一种使用 CSS 实现的波浪效果,思 ...
- leecode之Implement strStr()
KMP算法的实现: #include <stdio.h> #include <string.h> #include <stdlib.h> int strStr(ch ...
- C++PRIMER第二章前半部分答案
C++PRIMER第二章前半部分答案 哈哈哈,为什么是前半部分呢,后半部分还在学习中,重新系统性的学习c++,共同进步嘛,不多说,跟我一起来看看吧,第三章开始才是新手收割的时候,慢慢来~~ 2.1&a ...
- [ML] 高德软件的路径规划原理
路径规划 Dijkstra s:起点:S:已知到起点最短路径的点:U:未知到起点最短路径的点 Step 1:S中只有起点s,从U中找出路径最短的 Step 2:更新U中的顶点和顶点对应的路径 重复St ...
- Linux 用 ps 與 top 指令找出最耗費 CPU 與記憶體資源的程式最占cpu的进程
Linux 用 ps 與 top 指令找出最耗費 CPU 與記憶體資源的程式 2016/12/220 Comments ######### ps -eo pid,ppid,%mem,%cpu,cmd ...
- 1.7 Systemd初始化进程
1.7 Systemd初始化进程 Linux操作系统的开机过程是这样的,即从BIOS开始,然后进入Boot Loader,再加载系统内核,然后内核进行初始化,最后启动初始化进程.初始化进程作为Linu ...
- 1.5 RPM红帽软件包1.6 Yum软件仓库
1.5 RPM红帽软件包 在RPM(红帽软件包管理器)公布之前,要想在Linux系统中安装软件只能采取源码包的方式安装.早期在Linux系统中安装程序是一件非常困难.耗费耐心的事情,而且大多数的服务程 ...