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
13 17 17
維護向量序列,支持在序列末尾添加向量,以及詢問某個區間中的向量與給定向量的點積的最大值。算法
通常性設當前詢問的$y_0>0$,那麼$\dfrac{ans}{y_0}=\max\{\dfrac{x_0}{y_0}\cdot x+y\}$,而後這個東西和斜率優化長得同樣,答案必定是在凸殼上的.優化
因而咱們維護這個凸殼.由於有區間詢問因此線段樹維護每一個區間的凸殼.具體地,插入的時候統計當前區間已經有多少個點,若是點數等於當前區間長度那麼構造出這個區間的凸殼.詢問的時候拆成$\log$個區間分別跑二分/三分便可.加密
求凸包這裏使用按$x$座標排序的那個算法.注意若是幾個點的$x$相同那麼要按$y$排序.spa
插入的時候每一個區間只會被構建一次凸包,總複雜度$O(n\log n)$,排序用歸併.3d
查詢的時候拆成$\log$個區間,每一個區間$O(\log n)$三分/二分,總複雜度$O(n\log ^2 n)$指針
每一個區間都要構建一次凸包,能夠這麼處理:每一個線段樹節點放個指針,動態開闢空間創建凸包。code
網上很多代碼把狀況討論合併成一種,只維護上凸殼。保險起見,也爲了本身可以更好地理解,我仍是分類討論,同時維護上下凸殼。blog
#include<cstdio> #include<algorithm> using namespace std; typedef long long ll; inline void read(int &x){ register char ch=getchar();x=0;register bool f=0; for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=1; for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; if(f) x=-x; } const int N=4e5+3; int n,m,cnt;ll ans;char type[3],op[3]; struct Q{ int opt,l,r,x,y; Q(){} Q(int opt,int l,int r,int x,int y):opt(opt),l(l),r(r),x(x),y(y){} }q[N]; struct point{ int x,y; point(int x=0,int y=0):x(x),y(y){} point operator +(const point &a)const{ return point(x+a.x,y+a.y); } point operator -(const point &a)const{ return point(x-a.x,y-a.y); } ll operator *(const point &a)const{ return (ll)x*a.x+(ll)y*a.y; } ll operator ^(const point &a){ return (ll)x*a.y-(ll)y*a.x; } bool operator <(const point &a)const{ return x==a.x?y<a.y:x<a.x; } }p[N],tmp[N];int tmpsize; struct CH{ point *up,*dw; int upsize,dwsize; void init(int l,int r){ up=new point[r-l+2]; dw=new point[r-l+2]; tmpsize=upsize=dwsize=0; for(int i=l;i<=r;i++) tmp[++tmpsize]=p[i]; sort(tmp+1,tmp+tmpsize+1); for(int i=1;i<=tmpsize;i++){ for(;upsize>1&&((tmp[i]-up[upsize])^(up[upsize]-up[upsize-1]))<=0;upsize--); up[++upsize]=tmp[i]; for(;dwsize>1&&((dw[dwsize]-dw[dwsize-1])^(tmp[i]-dw[dwsize]))<=0;dwsize--); dw[++dwsize]=tmp[i]; } } ll qmax(point p){ int l,r,mid1,mid2;ll res=-(1LL<<62); if(p.y>=0){ l=1;r=upsize; while(r-l>2){ mid1=l+(r-l)/3; mid2=r-(r-l)/3; if(up[mid1]*p<up[mid2]*p) l=mid1; else r=mid2; } for(int i=l;i<=r;i++) res=max(res,up[i]*p); } else{ l=1;r=dwsize; while(r-l>2){ mid1=l+(r-l)/3; mid2=r-(r-l)/3; if(dw[mid1]*p<dw[mid2]*p) l=mid1; else r=mid2; } for(int i=l;i<=r;i++) res=max(res,dw[i]*p); } return res; } }b[N<<2];bool tag[N<<2]; #define lch k<<1 #define rch k<<1|1 ll query(int k,int l,int r,int x,int y,point p){ if(l==x&&r==y){ if(!tag[k]) tag[k]=1,b[k].init(l,r); return b[k].qmax(p); } int mid=l+r>>1; if(y<=mid) return query(lch,l,mid,x,y,p); else if(x>mid) return query(rch,mid+1,r,x,y,p); else return max(query(lch,l,mid,x,mid,p),query(rch,mid+1,r,mid+1,y,p)); } inline void decode(int &x){ if(type[0]=='E') return ; x=x^(ans&0x7fffffff); } int main(){ read(m);scanf("%s",type); for(int i=1,opt,x,y,l,r;i<=m;i++){ scanf("%s",op);opt=(op[0]=='Q'); if(opt){ read(x);read(y); read(l);read(r); } else{ read(x);read(y);l=r=0; ++n; } q[i]=Q(opt,l,r,x,y); } for(int i=1,l,r,x,y;i<=m;i++){ if(q[i].opt){ l=q[i].l;r=q[i].r; x=q[i].x;y=q[i].y; decode(l);decode(r); decode(x);decode(y); ans=query(1,1,n,l,r,point(x,y)); printf("%lld\n",ans); } else{ x=q[i].x;y=q[i].y; decode(x);decode(y); p[++cnt]=point(x,y); } } return 0; }
收穫:排序
全部形如$f[i]=\min\limits_{L(i)\leq j\leq R(i)}\{k(i)x(j)+F(j)\}+G(i)$的$dp$都是可作的(斜率優化型動態規劃)get