BZOJ3533:[SDOI2014]向量集(线段树,三分,凸包)

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);
}
其中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

Solution

设查询的点为$(a,b)$,那么我们需要在当前平面上找一个点$(x,y)$,使得$ax+by=c$的$c$最大化。

把式子化一下为$y=-\frac{a}{b}x+\frac{c}{b}$。

也就是斜率固定,我们要最大化$c$。比较显然的是这条直线肯定是在凸包上取到答案,现在的问题是怎么维护这个凸包。

可以发现因为我们只需要点积的最大值,并不需要凸包的具体形态,所以我们可以开一颗线段树,每个节点维护对应区间的凸包,查询时把区间对应到线段树上的$log$个区间然后取$max$就好了。

现在的问题是怎么修改。如果每次修改都重构线段树节点上的凸包的话,一次修改是$nlogn$的。

不过我们发现可以不用一次修改所有的节点,线段树上的一个区间,会被用到当且仅当这个区间内的点已经全被插入了。这样一次修改的复杂度就是均摊$logn$的了,复杂度证明还是比较显然的……

当$b>0$时,截距越大,$c$越大,我们在上凸壳上三分。

当$b<0$时,截距越小,$c$越大,我们在下凸壳上三分。

Code

  1 #include<iostream>
  2 #include<cstring>
  3 #include<cstdio>
  4 #include<vector>
  5 #include<cmath>
  6 #include<algorithm>
  7 #define N (400009) 
  8 #define LL long long
  9 using namespace std;
 10 
 11 struct Vector
 12 {
 13     LL x,y;
 14     Vector(double xx=0,double yy=0)
 15     {
 16         x=xx; y=yy;
 17     }
 18     bool operator < (const Vector &a) const
 19     {
 20         return x==a.x?y<a.y:x<a.x;
 21     }
 22 }a[N],P[N];
 23 typedef Vector Point;
 24 
 25 int n,x,y,l,r,cnt;
 26 LL ans;
 27 char s[2],opt[2];
 28 vector<Point>U[N<<2],D[N<<2];
 29 
 30 Vector operator - (Vector a,Vector b) {return Vector(a.x-b.x,a.y-b.y);}
 31 LL Cross(Vector a,Vector b) {return a.x*b.y-a.y*b.x;}
 32 LL Dot(Vector a,Vector b) {return a.x*b.x+a.y*b.y;}
 33 
 34 void decode(int &x)
 35 {
 36     x=x^(ans&0x7fffffff);
 37 }
 38 
 39 void ConvexHull(int now,int l,int r)
 40 {
 41     for (int i=l; i<=r; ++i) a[i]=P[i];
 42     sort(a+l,a+r+1);
 43     int h=0;
 44     for (int i=l; i<=r; ++i)
 45     {
 46         while (h>1 && Cross(a[i]-U[now][h-2],U[now][h-1]-U[now][h-2])<=0)
 47             h--, U[now].pop_back();
 48         h++; U[now].push_back(a[i]);
 49     }
 50     h=0;
 51     for (int i=l; i<=r; ++i)
 52     {
 53         while (h>1 && Cross(a[i]-D[now][h-2],D[now][h-1]-D[now][h-2])>=0)
 54             h--, D[now].pop_back();
 55         h++; D[now].push_back(a[i]);
 56     }
 57 }
 58 
 59 void Update(int now,int l,int r,int x)
 60 {
 61     if (l==r)
 62     {
 63         U[now].push_back(P[x]);
 64         D[now].push_back(P[x]);
 65         return;
 66     }
 67     int mid=(l+r)>>1;
 68     if (x<=mid) Update(now<<1,l,mid,x);
 69     else Update(now<<1|1,mid+1,r,x);
 70     if (x==r) ConvexHull(now,l,r);
 71 }
 72 
 73 LL Query(int now,int l,int r,int l1,int r1)
 74 {
 75     if (l>r1 || r<l1) return -1e18;
 76     if (l1<=l && r<=r1)
 77     {
 78         if (y>0)
 79         {
 80             int L=0,R=U[now].size()-1;
 81             Point p=Point(x,y);
 82             if (L==R) return Dot(p,U[now][L]);
 83             else if (L+1==R) return max(Dot(p,U[now][L]),Dot(p,U[now][R]));
 84             while (R-L>=3)
 85             {
 86                 int lmid=L+(R-L+1)/3,rmid=L+(R-L+1)/3*2;
 87                 LL ans1=Dot(p,U[now][lmid]);
 88                 LL ans2=Dot(p,U[now][rmid]);
 89                 if (ans1>ans2) R=rmid;
 90                 else L=lmid;
 91             }
 92             return max(Dot(p,U[now][L]),max(Dot(p,U[now][R]),Dot(p,U[now][R-1])));
 93         }
 94         else
 95         {
 96             int L=0,R=D[now].size()-1;
 97             Point p=Point(x,y);
 98             while (R-L>=3)
 99             {
100                 int lmid=L+(R-L+1)/3,rmid=L+(R-L+1)/3*2;
101                 LL ans1=Dot(p,D[now][lmid]);
102                 LL ans2=Dot(p,D[now][rmid]);
103                 if (ans1>ans2) R=rmid;
104                 else L=lmid;
105             }
106             return max(Dot(p,D[now][L]),max(Dot(p,D[now][R]),Dot(p,D[now][R-1])));
107         }
108     }
109     int mid=(l+r)>>1;
110     return max(Query(now<<1,l,mid,l1,r1),Query(now<<1|1,mid+1,r,l1,r1));
111 }
112 
113 int main()
114 {
115     scanf("%d%s",&n,s);
116     for (int i=1; i<=n; ++i)
117     {
118         scanf("%s",opt);
119         if (opt[0]=='A')
120         {
121             scanf("%d%d",&x,&y);
122             if (s[0]!='E') decode(x), decode(y);
123             P[++cnt]=Point(x,y);
124             Update(1,1,n,cnt);
125         }
126         else
127         {
128             scanf("%d%d%d%d",&x,&y,&l,&r);
129             if (s[0]!='E') decode(x), decode(y), decode(l), decode(r);
130             ans=Query(1,1,n,l,r);
131             printf("%lld\n",ans);
132         }
133     }
134 }
posted @ 2019-02-15 17:05  Refun  阅读(230)  评论(0编辑  收藏  举报