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
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
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 }