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
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<cmath>
#include<algorithm>
#define N (400009)
#define LL long long
using namespace std; struct Vector
{
LL x,y;
Vector(double xx=,double yy=)
{
x=xx; y=yy;
}
bool operator < (const Vector &a) const
{
return x==a.x?y<a.y:x<a.x;
}
}a[N],P[N];
typedef Vector Point; int n,x,y,l,r,cnt;
LL ans;
char s[],opt[];
vector<Point>U[N<<],D[N<<]; Vector operator - (Vector a,Vector b) {return Vector(a.x-b.x,a.y-b.y);}
LL Cross(Vector a,Vector b) {return a.x*b.y-a.y*b.x;}
LL Dot(Vector a,Vector b) {return a.x*b.x+a.y*b.y;} void decode(int &x)
{
x=x^(ans&0x7fffffff);
} void ConvexHull(int now,int l,int r)
{
for (int i=l; i<=r; ++i) a[i]=P[i];
sort(a+l,a+r+);
int h=;
for (int i=l; i<=r; ++i)
{
while (h> && Cross(a[i]-U[now][h-],U[now][h-]-U[now][h-])<=)
h--, U[now].pop_back();
h++; U[now].push_back(a[i]);
}
h=;
for (int i=l; i<=r; ++i)
{
while (h> && Cross(a[i]-D[now][h-],D[now][h-]-D[now][h-])>=)
h--, D[now].pop_back();
h++; D[now].push_back(a[i]);
}
} void Update(int now,int l,int r,int x)
{
if (l==r)
{
U[now].push_back(P[x]);
D[now].push_back(P[x]);
return;
}
int mid=(l+r)>>;
if (x<=mid) Update(now<<,l,mid,x);
else Update(now<<|,mid+,r,x);
if (x==r) ConvexHull(now,l,r);
} LL Query(int now,int l,int r,int l1,int r1)
{
if (l>r1 || r<l1) return -1e18;
if (l1<=l && r<=r1)
{
if (y>)
{
int L=,R=U[now].size()-;
Point p=Point(x,y);
if (L==R) return Dot(p,U[now][L]);
else if (L+==R) return max(Dot(p,U[now][L]),Dot(p,U[now][R]));
while (R-L>=)
{
int lmid=L+(R-L+)/,rmid=L+(R-L+)/*;
LL ans1=Dot(p,U[now][lmid]);
LL ans2=Dot(p,U[now][rmid]);
if (ans1>ans2) R=rmid;
else L=lmid;
}
return max(Dot(p,U[now][L]),max(Dot(p,U[now][R]),Dot(p,U[now][R-])));
}
else
{
int L=,R=D[now].size()-;
Point p=Point(x,y);
while (R-L>=)
{
int lmid=L+(R-L+)/,rmid=L+(R-L+)/*;
LL ans1=Dot(p,D[now][lmid]);
LL ans2=Dot(p,D[now][rmid]);
if (ans1>ans2) R=rmid;
else L=lmid;
}
return max(Dot(p,D[now][L]),max(Dot(p,D[now][R]),Dot(p,D[now][R-])));
}
}
int mid=(l+r)>>;
return max(Query(now<<,l,mid,l1,r1),Query(now<<|,mid+,r,l1,r1));
} int main()
{
scanf("%d%s",&n,s);
for (int i=; i<=n; ++i)
{
scanf("%s",opt);
if (opt[]=='A')
{
scanf("%d%d",&x,&y);
if (s[]!='E') decode(x), decode(y);
P[++cnt]=Point(x,y);
Update(,,n,cnt);
}
else
{
scanf("%d%d%d%d",&x,&y,&l,&r);
if (s[]!='E') decode(x), decode(y), decode(l), decode(r);
ans=Query(,,n,l,r);
printf("%lld\n",ans);
}
}
}
BZOJ3533:[SDOI2014]向量集(线段树,三分,凸包)的更多相关文章
- BZOJ 3533: [Sdoi2014]向量集( 线段树 + 三分 )
答案一定是在凸壳上的(y>0上凸壳, y<0下凸壳). 线段树维护, 至多N次询问, 每次询问影响O(logN)数量级的线段树结点, 每个结点O(logN)暴力建凸壳, 然后O(logN) ...
- bzoj 3533: [Sdoi2014]向量集 线段树维护凸包
题目大意: http://www.lydsy.com/JudgeOnline/problem.php?id=3533 题解: 首先我们把这些向量都平移到原点.这样我们就发现: 对于每次询问所得到的an ...
- bzoj 3533 [Sdoi2014]向量集 线段树+凸包+三分(+动态开数组) 好题
题目大意 维护一个向量集合,在线支持以下操作: "A x y (|x|,|y| < =10^8)":加入向量(x,y); "Q x y l r (|x|,|y| & ...
- 【bzoj3533】[Sdoi2014]向量集 线段树+STL-vector维护凸包
题目描述 维护一个向量集合,在线支持以下操作:"A x y (|x|,|y| < =10^8)":加入向量(x,y);"Q x y l r (|x|,|y| < ...
- BZOJ3533 [Sdoi2014]向量集 【线段树 + 凸包 + 三分】
题目链接 BZOJ3533 题解 我们设询问的向量为\((x_0,y_0)\),参与乘积的向量为\((x,y)\) 则有 \[ \begin{aligned} ans &= x_0x + y_ ...
- [SDOI2014][BZOJ3533] 向量集 [线段树+凸包]
题面 BZOJ传送门 思路 首先当然是推式子 对于一个询问点$(x_0,y_0$和给定向量$(x_1,y_1)$来说,点积这么表达: $A=x_0x_1+y_0y_1$ 首先肯定是考虑大小关系:$x_ ...
- bzoj3533: [Sdoi2014]向量集
Description 维护一个向量集合,在线支持以下操作:"A x y (|x|,|y| < =10^8)":加入向量(x,y);" Q x y l r (|x| ...
- 2019.02.26 bzoj4311: 向量(线段树分治+凸包)
传送门 题意: 支持插入一个向量,删去某一个现有的向量,查询现有的所有向量与给出的一个向量的点积的最大值. 思路: 考虑线段树分治. 先对于每个向量处理出其有效时间放到线段树上面,然后考虑查询:对于两 ...
- 【BZOJ4311】向量(线段树分治,斜率优化)
[BZOJ4311]向量(线段树分治,斜率优化) 题面 BZOJ 题解 先考虑对于给定的向量集,如何求解和当前向量的最大内积. 设当前向量\((x,y)\),有两个不同的向量\((u1,v1),(u2 ...
随机推荐
- office web app server部署和简单操作
部署环境:windows server 2012 R2,服务器在AD域中 参考网址: https://msdn.microsoft.com/zh-cn/magazine/jj219455(office ...
- Git(二)_基本命令
0. 开始查看所有配置:git config --listgit config --global user.name "runoob"git config --global use ...
- bootstrap-datepicker 开始时间-结束时间 thinkphp
<!DOCTYPE html> <head> <title>开始-结束时间测试</title> </head> <body> & ...
- 【Linux】CentOS安装solr 4.10.3
Solr是什么? Solr 是Apache下的一个顶级开源项目,采用Java开发,它是基于Lucene的全文搜索服务器.Solr提供了比Lucene更为丰富的查询语言,同时实现了可配置.可扩展,并对索 ...
- Java 强制类型转换
java提高篇(十一)-----强制类型转换 在java中强制类型转换分为基本数据类型和引用数据类型两种,这里我们讨论的后者,也就是引用数据类型的强制类型转换. 在Java中由于继承和向上转型,子类可 ...
- 排序算法(8)--Merge Sorting--归并排序--Merge sort--归并排序
1.基本思想 归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用.将已有序的子序列合并,得到完全有序的序列:即先使每个子序 ...
- 【代码笔记】iOS-产生随机字符串
一,代码: - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, ...
- 【代码笔记】iOS-NSJSONSerializationDemo
一,代码. - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. ...
- Mybatis中使用循环遍历
Mybatis中传参数的方法 1.使用注解绑定,mapper.xml 对应方法 不需要指定 parameterType,(推荐使用注解绑定方式) 方法接口: List<CalculateIdea ...
- 前端开发笔记(2)css基础(上)
CSS介绍 层叠样式表(英文全称:Cascading Style Sheets)是一种用来表现HTML(标准通用标记语言的一个应用)或XML(标准通用标记语言的一个子集)等文件样式的计算机语言.CSS ...