【SDOI2014】向量集(线段树)(凸壳二分)

传送门


题解:

其实这个写法二进制分组和ZKW线段树差别已经不大了。。。

直接建线段树,暴力insert,当一个区间满了的时候建立凸包即可,分上下两个凸壳来建。

询问直接在凸壳上二分即可。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

namespace IO{
	cs int Rlen=1<<22|1;
	char buf[Rlen],*p1,*p2;
	inline char gc(){return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;}
	inline char peek(){return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1;}
	inline char ga(){while(!isalpha(peek()))++p1;return *p1++;}
	
	template<typename T>
	inline T get(){
		char c;T num;bool f=0;
		while(!isdigit(c=gc()))f=c=='-';num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return f?-num:num;
	}
	inline int gi(){return get<int>();}
}
using namespace IO;

using std::cerr;
using std::cout;

struct Point{int x,y;};
inline ll operator*(cs Point &a,cs Point &b){return (ll)a.x*b.y-(ll)a.y*b.x;}
inline ll dot(cs Point &a,cs Point &b){return (ll)a.x*b.x+(ll)a.y*b.y;}
inline Point operator-(cs Point &a,cs Point &b){return (Point){a.x-b.x,a.y-b.y};}
inline Point operator-(cs Point &a){return (Point){-a.x,-a.y};}

cs int N=4e5+7,mx=1<<19;
Point nd[2][20][N],*hull[mx+N][2],*a;
int siz[mx+N][2],now,tp,n,op;ll ans;

inline void ins(cs Point &p){
	if(tp&&a[tp-1].x==p.x){
		if(a[tp-1].y>=p.y)return ;
		--tp;
	}
	while(tp>1&&(a[tp-2]-a[tp-1])*(p-a[tp-1])<=0)--tp;
	a[tp++]=p;
}
inline void build(int p){
	if(siz[p][0])return ;
	for(int re t=0;t<2;++t){
		a=hull[p][t],tp=0;
		Point *Lx=hull[p<<1][t],*cs Ly=Lx+siz[p<<1][t];
		Point *Rx=hull[p<<1|1][t],*cs Ry=Rx+siz[p<<1|1][t];
		while(Lx<Ly&&Rx<Ry)ins(Lx->x<Rx->x?*Lx++:*Rx++);
		while(Lx<Ly)ins(*Lx++);while(Rx<Ry)ins(*Rx++);
		siz[p][t]=tp;
	}
}

inline void push(){
	int x=gi(),y=gi(),q=mx+(++now);
	if(op)x^=ans,y^=ans;
	siz[q][0]=1;*hull[q][0]=(Point){x,y};
	siz[q][1]=1;*hull[q][1]=(Point){-x,-y};
	for(;q&1;q>>=1,build(q));
}

inline ll query(int q,Point p){
	int l=-1,r;
	if(p.y>=0)a=hull[q][0],r=siz[q][0]-1;
	else a=hull[q][1],p=-p,r=siz[q][1]-1;
	while(l+1<r){
		int mid=l+r>>1;
		if(dot(a[mid],p)>=dot(a[mid+1],p))r=mid;
		else l=mid;
	}
	return dot(a[r],p);
}
inline void query(){
	int x,y,l,r;
	if(op)x=gi()^ans,y=gi()^ans,l=(gi()^ans)+mx-1,r=(gi()^ans)+mx+1;
	else x=gi(),y=gi(),l=gi()+mx-1,r=gi()+mx+1;
	ans=-(1ll<<61);
	Point p=(Point){x,y};
	for(;l^r^1;l>>=1,r>>=1){
		if(~l&1)ans=std::max(ans,query(l^1,p));
		if(r&1) ans=std::max(ans,query(r^1,p));
	}
	cout<<ans<<"\n";ans&=0x7FFFFFFF;
}

signed main(){
#ifdef zxyoi
	freopen("vector.in","r",stdin);
#endif
	n=gi(),op=ga()!='E';
	for(int re l=mx,r=mx+n+10,d=0;l;l>>=1,r>>=1,++d)
	for(int re i=l;i<=r;++i)hull[i][0]=nd[0][d]+(i-l<<d),hull[i][1]=nd[1][d]+(i-l<<d);
	for(int re t=mx;t;t>>=1)siz[t][0]=siz[t-1][0]=siz[t][1]=siz[t-1][1]=-1;
	while(n--){
		switch(ga()){
			case 'A':push();break;
			case 'Q':query();break;
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值