CDQ分治——NKOJ3210 围牛群

题意:
维护一个点集,支持两种操作:
(1) 插入点P(x,y).
(2)询问所有点是否在 直线Ax+By=C的同一侧。
分析:
显然可以用平衡树维护凸包,每次二分查找切线。但是代码比较复杂。
考虑cdq分治,按照输入的顺序,左区间的点对右区间的询问都会有影响。所以直接求出左区间点构成的凸包,静态查询右边区间的直线是否切割凸包即可。
对于直线Ax+By+C=0,令f(x)=Ax+By+C. f(x)的正负对应了点和直线的位置关系。并且|f(x)|正比于点到直线的距离。所以凸包上的点的f(x)值一定是单峰函数,所以可以用三分法求出极值,再判断正负即可。
三分好容易写挂,注意特判一下端点。
由此可见,CDQ分治在这个问题上和 平衡树维护凸包其实是完全等价的。
代码如下:

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#define LL long long

using namespace std;
const int maxn=300000+5;
const LL inf= 999999999999999LL;

template <class T>
inline void _read(T &x){
    char ch=getchar(); bool mark=false;
    for(;!isdigit(ch);ch=getchar())if(ch=='-')mark=true;
    for(x=0;isdigit(ch);ch=getchar())x=x*10+ch-'0';
    if(mark)x=-x;
}

int n,qq,N;
//Definition of Point and Vector
struct Point {
    LL x,y;
    Point(){}
    Point (LL x,LL y):x(x),y(y){};
    bool operator < (const Point p)const {
        return x<p.x||(x==p.x&&y<p.y);
    }
    void put(){
        cout<<"("<<x<<","<<y<<")";
    }
};

typedef Point Vector;
Vector operator + (Vector A,Vector B){return Vector(A.x+B.x,A.y+B.y);}
Vector operator - (Vector A,Vector B){return Vector(A.x-B.x,A.y-B.y);}
LL Dot(Vector A,Vector B){return A.x*B.x+A.y*B.y;}
LL Cross(Vector A,Vector B){return A.x*B.y-A.y*B.x;}

//求凸包 返回 下凸包CH[0~rear1-1],上凸包CH[rear1~raer2-1]; 
Point CH[maxn];
int rear1,rear;
void ConvexHull(Point P[],int n){
    //cout<<"IN function ConvexHull: n= "<<n<<endl;
    //cout<<"Point[]:";for(int i=1;i<=n;i++)P[i].put();cout<<endl;
    sort(P+1,P+1+n);
    int i;
    rear=0;
    for(i=1;i<=n;i++){
        while(rear>1&&Cross(CH[rear-1]-CH[rear-2],P[i]-CH[rear-2])<=0) rear--;
        CH[rear++]= P[i];
    }
    rear1=rear;
    for(i=n-1;i>0;i--){
        while(rear>rear1 && Cross(CH[rear-1]-CH[rear-2],P[i]-CH[rear-2])<=0) rear--;
        CH[rear++]= P[i];
    }
    if(n>1) rear--;
    //cout<<"return with rear= "<<rear<<" and rear1= "<<rear1<<endl; 
}

struct operation{
    LL x,y,z,Max;
    int ans;
}op[maxn]; 

LL F(int l,Point p){
    return op[l].x*p.x+op[l].y*p.y+op[l].z;
}
LL f(LL x){if(!x)return 0 ;return x>0? 1:-1;}
//三分求 Max & Min 
Point Q[maxn];
LL GetMax(int L,int R,int l){
    if(R<L) return -inf;
    LL Max= max(F(l,CH[L]),F(l,CH[R]));
    while(true){
        if(R-L<=5){
            for(int i=L;i<=R;i++) Max=max(Max,F(l,CH[i]));
            return Max;
        }
        int lmid=L+(R-L+1)/3,rmid=R-(R-L+1)/3;
        LL f1=F(l,CH[lmid]),f2=F(l,CH[rmid]);
        if(f1>f2) R=rmid;
        else L=lmid;
    }
} 

LL GetMin(int L,int R,int l){
    if(R<L) return inf;
    LL Min= min(F(l,CH[L]),F(l,CH[R]));
    while(true){
        if(R-L<=5){
            for(int i=L;i<=R;i++) Min=min(Min,F(l,CH[i]));
            return Min;
        }
        int lmid=L+(R-L+1)/3,rmid=R-(R-L+1)/3;
        LL f1=F(l,CH[lmid]),f2=F(l,CH[rmid]);
        if(f1<f2) R=rmid;
        else L=lmid;
    }
} 

//CDQ
void Solve(int L,int R){
    if(L==R) return ;
    int mid=(L+R)>>1;
    Solve(L,mid);Solve(mid+1,R);
    int i,j,m=0;
    for(i=L;i<=mid;i++)
        if(op[i].ans==-1) Q[++m]=Point(op[i].x,op[i].y);
    if(!m) return ;
    ConvexHull(Q,m);
    for(i=mid+1;i<=R;i++)
        if(op[i].ans==0){
            LL Max=max( GetMax(0,rear1-1,i), GetMax(rear1,rear-1,i) );
            LL Min=min( GetMin(0,rear1-1,i), GetMin(rear1,rear-1,i) );
            if(Max==0||Min==0||f(Max)*f(Min)<0|| (f(Max)*f(op[i].Max)<0) )
                op[i].ans=1;
            op[i].Max= f(Max);
        }
}

int main(){
    int i,j,x,y,k;
    _read(n); _read(qq);
    for(i=1;i<=n;i++){
        _read(op[i].x); _read(op[i].y);
        op[i].ans=-1;
    }
    N=qq+n;
    for(i=n+1;i<=N;i++){
        _read(k);
        if(k==1){
            _read(op[i].x); _read(op[i].y);
            op[i].ans=-1;
        }
        else {
            _read(op[i].x); _read(op[i].y); _read(op[i].z);
            op[i].z*=-1;
        }
    }
    Solve(1,N);
    for(i=1;i<=N;i++)
        if(op[i].ans!=-1)
            puts(op[i].ans==0? "YES":"NO");
    return 0;
}
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cmath>
#define ll long long
using namespace std;
const ll inf=1000000000000000LL;
template <typename T>
inline void _read(T& x){
    char t=getchar();bool sign=true;
    while(t<'0'||t>'9')
    {if(t=='-')sign=false;t=getchar();}
    for(x=0;t>='0'&&t<='9';t=getchar())x=x*10+t-'0';
    if(!sign)x=-x;
}
int n,q;
struct point{
    ll x,y;
    point(){}
    point(ll g,ll h){x=g;y=h;}
    bool operator < (const point G) const {
        if(x==G.x)return y<G.y;
        else return x<G.x;
    }
    point operator + (const point G) const {
        return point(x+G.x,y+G.y);
    }
    point operator - (const point G) const {
        return point(x-G.x,y-G.y);
    }
};
ll dot(point a,point b){
    return a.x*b.x+a.y*b.y;
}
ll cross(point a,point b){
    return a.x*b.y-a.y*b.x;
}
double len(point a){
    return sqrt(a.x*a.x+a.y*a.y);
}
struct node{
    ll type,a,b,c,ans,last;
}; 
node work[500005];

ll f(point a,int t){
    return a.x*work[t].a+a.y*work[t].b+work[t].c;
}

point p[500005];
int down,up;
void convex_closure(point s[],int k){
    int i,j;
    sort(s+1,s+1+k);
    down=up=0; 
    for(i=1;i<=k;i++){
        while(up>1&&cross(p[up-1]-p[up-2],s[i]-p[up-2])<=0)up--;
        p[up++]=s[i];
    }
    down=up;
    for(i=k-1;i;i--){
        while(up>down&&cross(p[up-1]-p[up-2],s[i]-p[up-2])<=0)up--;
        p[up++]=s[i];
    }
    if(k>1)up--;
}
ll getmax(int l,int r,int t){
    if(r<l)return -inf;
    ll maxn=-inf;
    maxn=max(f(p[l],t),f(p[r],t));
    int i,j,k;
    for(;;){
        if(r-l<=6){
            for(i=l;i<=r;i++){
                maxn=max(maxn,f(p[i],t));
            }
            return maxn;
        }
        int lmid=l+(r-l+1)/3,rmid=lmid+(r-l+1)/3;
        if(f(p[lmid],t)>f(p[rmid],t))r=rmid;
        else l=lmid;
    }
}
ll getmin(int l,int r,int t){
    if(r<l)return inf;
    ll minn=inf;
    minn=min(f(p[l],t),f(p[r],t));
    int i,j,k;
    for(;;){
        if(r-l<=6){
            for(i=l;i<=r;i++){
                minn=min(minn,f(p[i],t));
            }
            return minn;
        }
        int lmid=l+(r-l+1)/3,rmid=lmid+(r-l+1)/3;
        if(f(p[lmid],t)<f(p[rmid],t))r=rmid;
        else l=lmid;
    }
}
point temp[200005];
void cdq(int l,int r){
    if(l==r)return;
    int mid=(l+r)>>1;
    //cout<<"cdq("<<l<<","<<r<<")"<<endl;
    cdq(l,mid);cdq(mid+1,r);
    int i,j,k;
    k=0;
    for(i=l;i<=mid;i++){
        if(work[i].type==1)temp[++k]=point(work[i].a,work[i].b);
    } 
    if(k==0)return;
    convex_closure(temp,k);
    for(i=mid+1;i<=r;i++){
        if(work[i].type==2){
            ll maxn,minn;
            maxn=max(getmax(0,down-1,i),getmax(down,up-1,i));
            minn=min(getmin(0,down-1,i),getmin(down,up-1,i));
            if(maxn>0)maxn=1;
            if(maxn<0)maxn=-1;
            if(minn>0)minn=1;
            if(minn<0)minn=-1;
            if(maxn==0||minn==0||(minn*maxn)<0||(maxn*work[i].last)<0)work[i].ans=-1;
            work[i].last=maxn;
        }
    }
}
int main(){
    int i,j,k;
    cin>>n>>q;
    for(i=1;i<=n;i++){
        work[i].type=1;
        _read(work[i].a);_read(work[i].b);
    }
    for(i=n+1;i<=n+q;i++){
        _read(k);
        if(k==1){
            work[i].type=1;
            _read(work[i].a);_read(work[i].b);
        }
        if(k==2){
            work[i].type=2;
            _read(work[i].a);_read(work[i].b);_read(work[i].c);
            work[i].c=-work[i].c;
        }
    }
    cdq(1,n+q);
    for(i=n+1;i<=n+q;i++){
        if(work[i].type==2){
            if(work[i].ans==-1)puts("NO");
            else puts("YES");
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值