链接
http://www.lydsy.com/JudgeOnline/problem.php?id=3533
题解
QAQ以后再也不乱写模板了,因为Granham的各种错误查了1天,所有错误都是Graham里的…
通过做这道题目学会了三分。
就是l,mid1,mid2,r,可以三分出单峰函数的极值,循环条件是(r-l>=3)。
通过这道题还发现一个事实,如果每个线段树上的节点都存
O(size)
大小的数据,那么总内存占用只是
O(NlogN)
级别的。这个事实可以帮助我们做这道题目。
把向量看成坐标系中的点,对于一次查询
Q(xq,yq)
,如果
yq>0
,画图可知,答案在上凸壳上。(连接
(0,0)−(xq,yq)
然后做垂线,假设一个点是
P(xp,yp)
,移动垂线使得
OP→
在
OQ→
上的投影最大)不知道说的明白不明白,反正就那个意思…然后就发现答案就是从无穷远处移动垂线碰到的第一个点。当
yq>0
时,答案在上凸壳上,否则在下凸壳上(=0的情况归为哪一类都行)
如果给你一个凸壳,想要求查询的答案的话,显然答案是单峰的(画图可知),所以就可以三分。
现在要解决的问题是询问的区间
[l,r]
,直接通过线段树分配到某几个区间上,一次查询的复杂度是
log2
。
问题在于,怎么添加点。
显然单点插入,每个点只会插入
logN
个节点里,如果每次暴力
Graham Scan
重构的话,一次插入的复杂度就成了
NlogN
,显然太慢。
容易看到,只有插入操作,而且每次都是在末端插入,一个询问会被线段树分配成若干个区间,这些区间显然都是完整的、所有元素都被插入过的。
也就是说,一个线段树上的点只有所有的元素被插入了之后,才有被查询到的可能。那么,当我们插入这个节点上的最后一个节点的时候,再调用
Graham Scan
构建凸壳。显然每个节点都只会构造一次凸包。
这样的复杂度是
O(Nlog2N+Qlog2N)
的。
如果把
Graham Scan
做凸包改成斜率优化那样的单调栈维护凸壳,复杂度会变成
O(NlogN+Qlog2N)
代码
//线段树+凸包+三分
#include <cstdio>
#include <algorithm>
#include <queue>
#include <vector>
#include <cmath>
#define ll long long
#define maxn 400005
#define inf ((long long)1<<60)
using namespace std;
ll T, Mi, lastans, ndtot;
struct vec{ll x, y;}tmp[maxn], s[maxn];
struct segtree
{
ll l, r, jie;
segtree *ch[2];
vector<vec>pt;
}pool[maxn<<2], *root;
inline vec operator-(vec &v1, vec &v2){return (vec){v1.x-v2.x,v1.y-v2.y};}
inline ll operator*(vec v1, vec v2){return v1.x*v2.y-v2.x*v1.y;}
inline ll cp(vec v1, vec v2){return v1.x*v2.x+v1.y*v2.y;}
inline bool operator<(vec v1, vec v2)
{
v1=v1-s[1], v2=v2-s[1];
return v1*v2==0?v1.x*v1.x+v1.y*v1.y<v2.x*v2.x+v2.y*v2.y:v1*v2>0;
}
inline ll read(ll x=0)
{
char c=getchar(); ll f=1;
while(c<48 or c>57)f=c=='-'?-1:1,c=getchar();
while(c>=48 and c<=57)x=(x<<1)+(x<<3)+c-48, c=getchar();
x*=f;
if(Mi)x^=(lastans&0x7fffffff);
return x;
}
inline void graham(segtree *p)
{
ll i, j, top=1, sz=p->r-p->l+1;
for(i=1;i<=sz;i++)tmp[i]=p->pt[i-1];
for(j=1,i=2;i<=sz;i++)
if(tmp[i].x<tmp[j].x or tmp[i].x==tmp[j].x and tmp[i].y<tmp[j].y)j=i;
s[1]=tmp[j];swap(tmp[1],tmp[j]);
sort(tmp+2,tmp+sz+1);
for(i=2;i<=sz;i++)
{
while(top>1 and (tmp[i]-s[top])*(s[top]-s[top-1])>=0)top--;
s[++top]=tmp[i];
}
s[++top]=s[1];
p->pt.clear();
for(i=1;i<=top;i++)p->pt.push_back(s[i]);
for(i=1;i<top;i++)if(s[i+1].x<=s[i].x)break;
p->jie=i-1;
}
inline ll solve(segtree *p, vec pt)
{
ll l, r, mid1, mid2;
ll ans=-inf;
if(pt.y>0)l=p->jie,r=p->pt.size()-1;
else l=0,r=p->jie;
while(r-l>=3)
{
mid1=l+(r-l)/3, mid2=r-(r-l)/3;
if(cp(pt,p->pt[mid1])>cp(pt,p->pt[mid2]))r=mid2;else l=mid1;
}
for(ll i=l;i<=r;i++)ans=max(ans,cp(pt,p->pt[i]));
return ans;
}
void segins(segtree *p, ll pos, vec pt)
{
p->pt.push_back(pt);
if(pos==p->r)graham(p);
if(p->l==p->r)return;
ll mid=(p->l+p->r)>>1;
if(pos<=mid)segins(p->ch[0],pos,pt);
if(pos>mid)segins(p->ch[1],pos,pt);
}
ll segmax(segtree *p, ll l, ll r, vec pt)
{
ll mid=(p->l+p->r)>>1;
ll ans=-inf;
if(l<=p->l and r>=p->r)return solve(p,pt);
if(l<=mid)ans=max(ans,segmax(p->ch[0],l,r,pt));
if(r>mid)ans=max(ans,segmax(p->ch[1],l,r,pt));
return ans;
}
void build(segtree *p, ll l, ll r)
{
ll mid=(l+r)>>1;
p->l=l, p->r=r;
if(l==r)return;
build(p->ch[0]=pool+ ++ndtot,l,mid);
build(p->ch[1]=pool+ ++ndtot,mid+1,r);
}
void work()
{
char s[10];
ll i, l, r, T=0, N;
vec pt;
N=read();scanf("%s",s);Mi=s[0]!='E';
build(root=pool+ ++ndtot,1,N);
for(i=1;i<=N;i++)
{
scanf("%s",s);
pt.x=read(),pt.y=read();
if(s[0]=='Q')
{
l=read(),r=read();
printf("%lld\n",lastans=segmax(root,l,r,pt));
}
else{segins(root,++T,pt);}
}
}
int main()
{
work();
return 0;
}