P3309-[SDOI2014]向量集【线段树,凸壳】

1|0正题

题目链接:https://www.luogu.com.cn/problem/P3309


1|1题目大意

n个操作

  1. 在序列末尾加入一个向量(x,y)
  2. 询问加入的第lr个向量中的一个向量和(x,y)的点积最大值

强制在线,点积的定义为x1x2+y1y2


1|2解题思路

如果对于一个(x,y)对于两个(x1,y1)(x2,y2)如果后者更大那么有

x2x+y2y>x1x+y1yyxx2x1y2y1

好像和斜率有关,可以维护凸壳来做,因为y可能是负数,如果是负数的时候就要求的是下凸壳了,所以两个凸壳都要维护。

因为强制在线所以上不了传统艺能CDQ

那怎么动态维护区间凸壳,平衡树支持动态插入但不支持区间问题。所以考虑线段树,因为一个位置修改了之后就不会再修改,而且是从左往右加的,可以利用这个性质。

每次我们修改一个位置后,如果一个区间[L,R]的节点内已经插入了RL+1个向量(也就是都插完了)的话就直接把它的两个儿子的凸壳合并起来。

然后询问的时候分成log n个区间询问的答案取最大值就好了。

合并凸壳的是用归并排序的话时间复杂度O(nlogn)


1|3code

#include<cstdio> #include<cstring> #include<algorithm> #include<vector> #define ll long long using namespace std; struct point{ ll x,y; point(ll xx=0,ll yy=0) {x=xx;y=yy;return;} }z; point operator+(point x,point y) {return point(x.x+y.x,x.y+y.y);} point operator-(point x,point y) {return point(x.x-y.x,x.y-y.y);} ll operator^(point x,point y) {return x.x*y.y-x.y*y.x;} ll operator*(point x,point y) {return x.x*y.x+x.y*y.y;} bool operator<(point x,point y) {return (x.x==y.x)?x.y<y.y:x.x<y.x;} const ll N=4e5+10; char pe[3]; ll n,num,siz[N<<2]; vector<point> v[N<<2][2],tmp; void Make(ll x){ ll ls=x*2,rs=x*2+1; for(ll k=0;k<2;k++){ ll i=0,j=0,l1=v[ls][k].size()-1,l2=v[rs][k].size()-1; tmp.clear(); while(i<=l1||j<=l2){ if(i>l1||(j<=l2&&v[rs][k][j]<v[ls][k][i])) tmp.push_back(v[rs][k][j]),j++; else tmp.push_back(v[ls][k][i]),i++; } ll cnt=0; for(ll i=0;i<tmp.size();i++){ while(cnt>1&&((v[x][k][cnt-1]-v[x][k][cnt-2])^(tmp[i]-v[x][k][cnt-1]))>=0) v[x][k].pop_back(),cnt--; v[x][k].push_back(tmp[i]);cnt++; } } return; } ll Calc(ll x,point p){ ll f=0; if(p.y<0)p=z-p,f^=1; ll l=0,r=v[x][f].size()-2; while(l<=r){ ll mid=(l+r)>>1; point tmp=v[x][f][mid+1]-v[x][f][mid];tmp.x*=-1; if(p.x*tmp.x>=p.y*tmp.y)r=mid-1; else l=mid+1; } return p*v[x][f][l]; } void Change(ll x,ll L,ll R,ll pos,point p){ if(L==R){ v[x][0].push_back(p); v[x][1].push_back(z-p); return; } ll mid=(L+R)>>1;siz[x]++; if(pos<=mid)Change(x*2,L,mid,pos,p); else Change(x*2+1,mid+1,R,pos,p); if(siz[x]==R-L+1)Make(x); } ll Ask(ll x,ll L,ll R,ll l,ll r,point p){ if(L==l&&R==r)return Calc(x,p); ll mid=(L+R)>>1; if(r<=mid)return Ask(x*2,L,mid,l,r,p); if(l>mid)return Ask(x*2+1,mid+1,R,l,r,p); return max(Ask(x*2,L,mid,l,mid,p),Ask(x*2+1,mid+1,R,mid+1,r,p)); } void dc(ll &x,ll lastans) { if(pe[0]=='E')return; x=x^(lastans&0x7fffffff); return; } signed main() { scanf("%lld%s",&n,pe); ll last=0; for(ll i=1;i<=n;i++){ char op[3];ll x,y,l,r; scanf("%s%lld%lld",op,&x,&y); dc(x,last);dc(y,last); if(op[0]=='A'){ ++num; Change(1,1,n,num,point(x,y)); } else{ scanf("%lld%lld",&l,&r);dc(l,last);dc(r,last); printf("%lld\n",last=Ask(1,1,n,l,r,point(x,y))); } } return 0; }

__EOF__

本文作者QuantAsk
本文链接https://www.cnblogs.com/QuantAsk/p/14298414.html
关于博主:退役OIer,GD划水选手
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   QuantAsk  阅读(66)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示