3533: [Sdoi2014]向量集
Time Limit: 25 Sec Memory Limit: 512 MBSubmit: 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
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
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;
}