bzoj3533【SDOI2014】向量集

3533: [Sdoi2014]向量集

Time Limit: 25 Sec   Memory Limit: 512 MB
Submit: 669   Solved: 213
[ Submit][ Status][ Discuss]

Description

维护一个向量集合,在线支持以下操作:
"A x y (|x|,|y| < =10^8)":加入向量(x,y);
" Q x y l r (|x|,|y| < =10^8,1 < =L < =R < =T,其中T为已经加入的向量个数)询问第L个到第R个加入的向量与向量(x,y)的点积的最大值。
    集合初始时为空。

Input

    输入的第一行包含整数N和字符s,分别表示操作数和数据类别;
    接下来N行,每行一个操作,格式如上所述。
    请注意s≠'E'时,输入中的所有整数都经过了加密。你可以使用以下程序
得到原始输入:
inline int decode (int x long long lastans) {
     return x ^ (lastans & Ox7fffffff);
}
function decode
begin
    其中x为程序读入的数,lastans为之前最后一次询问的答案。在第一次询问之前,lastans=0。

注:向量(x,y)和(z,W)的点积定义为xz+yw。

Output

  对每个Q操作,输出一个整数表示答案。

Sample Input

6 A
A 3 2
Q 1 5 1 1
A 15 14
A 12 9
Q 12 8 12 15
Q 21 18 19 18

Sample Output

13
17
17

解释:解密之后的输入为
6 E
A 3 2
Q 1 5 1 1
A 2 3
A 1 4
Q 1 5 1 2
Q 4 3 2 3

HINT

1 < =N < =4×10^5


新加数据一组..2015.315

Source




线段树+凸包+三分,思路好题

有一个结论:答案一定会出现在凸包上,而且如果y>0则在上凸包上,否则在下凸包上。

简单证明:要计算一个向量a和一些向量的点积的最大值。因为所有向量都是共起点的,所以只要找到这些向量终点中在向量a的方向最靠前的,而这个最靠前点一定在凸包上。

然后在一个凸包上答案是单峰的,可以用三分来做。

于是问题就变成了如何动态维护区间的凸包,这个可以用线段树。

可是问题在于每次新加一个点会修改logn段区间,而凸包合并是O(n),显然这个复杂度不能接受。

有一个比较巧妙的解决方法,包含未插入位置的线段树节点一定不会被访问(这显然),所以每插入一个点x,只要将区间右端点是x的凸包合并就可以了。




#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
#define F(i,j,n) for(int i=j;i<=n;i++)
#define D(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
#define maxn 400005
#define inf 1000000000000000000ll
using namespace std;
int n,tot;
ll ans;
struct P
{
	ll x,y;
	P(ll xx=0,ll yy=0){x=xx;y=yy;}
	friend P operator +(P a,P b){return P(a.x+b.x,a.y+b.y);}
	friend P operator -(P a,P b){return P(a.x-b.x,a.y-b.y);}
	friend ll operator *(P a,P b){return a.x*b.y-a.y*b.x;}
	friend ll operator ^(P a,P b){return a.x*b.x+a.y*b.y;}
	friend bool operator <(P a,P b){return a.x==b.x?a.y<b.y:a.x<b.x;}
};
vector<P> a[maxn*4],b[maxn*4];
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
inline int decode(int x){return x^(ans&0x7fffffff);}
void merge_a(vector<P> &h1,vector<P> &h2,vector<P> &h)
{
	vector<P>::iterator i=h1.begin(),j=h2.begin();
	int top=0;P p;
	while (i!=h1.end()||j!=h2.end())
	{
		if (i==h1.end()) p=*(j++);
		else if (j==h2.end()) p=*(i++);
		else p=*i<*j?*(i++):*(j++);
		while (top>=2&&(h[top-1]-h[top-2])*(p-h[top-2])>=0) h.pop_back(),top--;
		h.push_back(p);top++;
	}
}
void merge_b(vector<P> &h1,vector<P> &h2,vector<P> &h)
{
	vector<P>::iterator i=h1.begin(),j=h2.begin();
	int top=0;P p;
	while (i!=h1.end()||j!=h2.end())
	{
		if (i==h1.end()) p=*(j++);
		else if (j==h2.end()) p=*(i++);
		else p=*i<*j?*(i++):*(j++);
		while (top>=2&&(h[top-1]-h[top-2])*(p-h[top-2])<=0) h.pop_back(),top--;
		h.push_back(p);top++;
	}
}
ll sanfen(vector<P> &h,P p)//这里不加& 会TLE 
{
	int l=0,r=h.size()-1,lmid,rmid;
	while (r-l>2)
	{
		lmid=(l*2+r)/3;rmid=(l+r*2)/3;
		if ((h[lmid]^p)>(h[rmid]^p)) r=rmid;
		else l=lmid;
	}
	ll ret=-inf;
	F(i,l,r) ret=max(ret,(h[i]^p));
	return ret;
}
void insert(int k,int l,int r,int pos,P p)
{
	if (l==r)
	{
		a[k].push_back(p);
		b[k].push_back(p);
		return;
	}
	int mid=(l+r)>>1;
	if (pos<=mid) insert(k<<1,l,mid,pos,p);
	else insert(k<<1|1,mid+1,r,pos,p);
	if (r==pos)
	{
		merge_a(a[k<<1],a[k<<1|1],a[k]);
		merge_b(b[k<<1],b[k<<1|1],b[k]);
	}
}
ll query(int k,int l,int r,int L,int R,P p)
{
	if (l==L&&r==R) return p.y>0?sanfen(a[k],p):sanfen(b[k],p);
	int mid=(l+r)>>1;
	if (R<=mid) return query(k<<1,l,mid,L,R,p);
	if (L>mid) return query(k<<1|1,mid+1,r,L,R,p);
	return max(query(k<<1,l,mid,L,mid,p),query(k<<1|1,mid+1,r,mid+1,R,p));
}
int main()
{
	char flg[10],opt[10];
	n=read();scanf("%s",flg);
	F(i,1,n)
	{
		scanf("%s",opt);
		if (opt[0]=='A')
		{
			int x=read(),y=read();
			if (flg[0]!='E') x=decode(x),y=decode(y);
			insert(1,1,n,++tot,P(x,y));
		}
		else
		{
			int x=read(),y=read(),l=read(),r=read();
			if (flg[0]!='E') x=decode(x),y=decode(y),l=decode(l),r=decode(r);
			printf("%lld\n",ans=query(1,1,n,l,r,P(x,y)));
		}
	}
	return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值