凸包(壳)算法小结
凸包专题大概是我做的最吃shi考验代码能力的专题……
然后……大概我们的凸包可以分为静态凸包和动态凸包,从功能上可以分为决策性的凸包和计算几何性的凸包
其实没有多少区别,打就好了
静态凸包啥的我就不介绍怎么打了……
然后我推荐在弹栈的时候用叉积而不是暴力算斜率,那样讨论好多啊……
另外,一般我们不会被卡弹栈……但如果被卡时间的话,我们可以考虑二分弹栈,这样并没有什么问题……
然后我们来看看凸包可以干什么:
然后我一开始想歪了……我想直接把(x,y)和(p,q)当作点的坐标然后去凸壳查找
但是这样的决策点是无法定义最优的……在不同的询问点中最优是不同的……很是尴尬
然后我这么弱当然%了题解啊……
所以我们可以考虑01分数规划,二分答案k,把原来的式子变形得到
$ y_{i}-k*x_{i}+q_{i}-k*p_{i}>=0 $
然后询问的时候,我们这就是维护一个上凸壳拿一条直线卡一下
可以联系一下高中线性规划的知识
然后我们可以用熟练剖分来解决这个问题,对于链上log次询问我们取最优的最优点即可
这样的复杂度……是$mlog^{4}n$的,但是由于没啥常数,并且数据范围还很小,所以说可以过30000的全部数据
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 #define N 30010 6 #define db double 7 int e,adj[N],n; 8 db x[N],y[N],p[N],q[N]; 9 struct edge{int zhong,next;}s[N<<1]; 10 inline void add(int qi,int zhong) 11 {s[++e].zhong=zhong;s[e].next=adj[qi];adj[qi]=e;} 12 int deep[N],son[N],size[N],fa[N],dfn[N],num,top[N],anti[N]; 13 inline void dfs1(int rt,int Vater) 14 { 15 fa[rt]=Vater;deep[rt]=deep[Vater]+1,size[rt]=1; 16 register int i,u; 17 for(i=adj[rt];i;i=s[i].next) 18 if((u=s[i].zhong)!=Vater) 19 { 20 dfs1(u,rt),size[rt]+=size[u]; 21 if(size[u]>size[son[rt]])son[rt]=u; 22 } 23 } 24 inline void dfs2(int rt,int tp) 25 { 26 dfn[rt]=++num,top[rt]=tp;anti[num]=rt; 27 if(son[rt])dfs2(son[rt],tp); 28 register int i,u; 29 for(i=adj[rt];i;i=s[i].next) 30 if((u=s[i].zhong)!=fa[rt]&&u!=son[rt])dfs2(u,u); 31 } 32 struct pt 33 { 34 db x,y;pt(db a=0,db b=0){x=a,y=b;} 35 inline pt operator + (const pt &b) const {return pt(x+b.x,y+b.y);} 36 inline pt operator - (const pt &b) const {return pt(x-b.x,y-b.y);} 37 inline db operator * (const pt &b) const {return x*b.y-y*b.x;} 38 }Mem1[N<<4],*head1=Mem1,Mem2[N<<4],*head2=Mem2,sta[N]; 39 struct node 40 { 41 node *ch[2]; 42 pt *xy,*pq; 43 int lxy,lpq; 44 }*root,mem[N<<1];int tot; 45 inline bool mt1(const pt &a,const pt &b){return a.x==b.x?a.y<b.y:a.x<b.x;} 46 #define inf 0x7fffffff 47 inline double max(db a,db b){return a>b?a:b;} 48 inline int solve(pt *x,int len) 49 { 50 sort(x,x+len,mt1); 51 register int i,top=-1; 52 for(i=0;i<len;++i) 53 { 54 while(top>0&&(sta[top]-sta[top-1])*(x[i]-sta[top])>=0)--top; 55 sta[++top]=x[i]; 56 } 57 for(i=0;i<=top;++i)x[i]=sta[i]; 58 return top; 59 } 60 inline db query(pt *x,db k,int len) 61 { 62 if(len==0)return x[0].y-k*x[0].x; 63 int l=0,r=len-1,mi,ans=0; 64 pt q=pt(1,k),tmp; 65 while(l<=r) 66 { 67 mi=(l+r)>>1,tmp=x[mi+1]-x[mi]; 68 if(tmp*q>=0)ans=mi,r=mi-1; 69 else l=mi+1; 70 } 71 return max(x[ans].y-k*x[ans].x,x[ans+1].y-k*x[ans+1].x); 72 } 73 inline node* build(int l,int r) 74 { 75 node *o=mem+(tot++); 76 o->xy=head1,o->pq=head2; 77 head1+=r-l+1,head2+=r-l+1; 78 for(int i=l;i<=r;++i) 79 o->xy[i-l]=pt(x[anti[i]],y[anti[i]]),o->pq[i-l]=pt(p[anti[i]],q[anti[i]]); 80 o->lxy=solve(o->xy,r-l+1),o->lpq=solve(o->pq,r-l+1); 81 if(l==r)return o; 82 register int mi=(l+r)>>1; 83 o->ch[0]=build(l,mi),o->ch[1]=build(mi+1,r); 84 return o; 85 } 86 inline double qxy(node *o,int l,int r,int L,int R,db k) 87 { 88 if(L<=l&&r<=R)return query(o->xy,k,o->lxy); 89 register int mi=(l+r)>>1; 90 db ret=-inf; 91 if(L<=mi)ret=qxy(o->ch[0],l,mi,L,R,k); 92 if(mi<R)ret=max(ret,qxy(o->ch[1],mi+1,r,L,R,k)); 93 return ret; 94 } 95 inline double qpq(node *o,int l,int r,int L,int R,db k) 96 { 97 if(L<=l&&r<=R)return query(o->pq,k,o->lpq); 98 register int mi=(l+r)>>1; 99 db ret=-inf; 100 if(L<=mi)ret=qpq(o->ch[0],l,mi,L,R,k); 101 if(mi<R)ret=max(ret,qpq(o->ch[1],mi+1,r,L,R,k)); 102 return ret; 103 } 104 #define eps 1e-5 105 inline db queryxy(int a,int b,db k) 106 { 107 db ret=-inf; 108 while(top[a]^top[b]) 109 { 110 if(deep[top[a]]<deep[top[b]])a^=b,b^=a,a^=b; 111 ret=max(ret,qxy(root,1,n,dfn[top[a]],dfn[a],k)),a=fa[top[a]]; 112 } 113 if(deep[a]<deep[b])a^=b,b^=a,a^=b; 114 return max(ret,qxy(root,1,n,dfn[b],dfn[a],k)); 115 } 116 inline db querypq(int a,int b,db k) 117 { 118 db ret=-inf; 119 while(top[a]^top[b]) 120 { 121 if(deep[top[a]]<deep[top[b]])a^=b,b^=a,a^=b; 122 ret=max(ret,qpq(root,1,n,dfn[top[a]],dfn[a],k)),a=fa[top[a]]; 123 } 124 if(deep[a]<deep[b])a^=b,b^=a,a^=b; 125 return max(ret,qpq(root,1,n,dfn[b],dfn[a],k)); 126 } 127 int main() 128 { 129 // freopen("Ark.in","r",stdin); 130 register int i,m,a,b;scanf("%d",&n); 131 for(i=1;i<=n;++i)scanf("%lf",&x[i]); 132 for(i=1;i<=n;++i)scanf("%lf",&y[i]); 133 for(i=1;i<=n;++i)scanf("%lf",&p[i]); 134 for(i=1;i<=n;++i)scanf("%lf",&q[i]); 135 for(i=1;i<n;++i)scanf("%d%d",&a,&b),add(a,b),add(b,a); 136 dfs1(1,0),dfs2(1,1),root=build(1,n); 137 db l,r,mi,ans;scanf("%d",&m); 138 while(m--) 139 { 140 scanf("%d%d",&a,&b); 141 l=0,r=1e5,ans=0; 142 while(r-l>eps) 143 { 144 mi=(l+r)/2; 145 if(queryxy(a,b,mi)+querypq(a,b,mi)>=0)l=mi,ans=mi; 146 else r=mi; 147 } 148 printf("%.4lf\n",ans); 149 } 150 }
还有比较shi的题目……
这个题你发现是“区间加等比数列,区间询问最大值”
所以很sad的是我们没办法用线段树……
那么我们考虑分块……毕竟树状数据结构不能用了
我们可以发现,如果我们区间加等比数列的话,按照下标为x坐标,当前美观度为y坐标建一个凸壳的话,
原来不在凸壳上面的点现在不会进入凸壳……如果公差>0,后面比他优的还比它优,公差<0前面比他优的还比它优……
所以我们可以再去查找凸包的最高点……
然后暴力重构……等等分块需要的操作
难调……最后改过了
二分和三分找最高点都是可以的
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <cmath> 4 using namespace std; 5 #define LL long long 6 #define L 350 7 #define N 100010 8 #define INF 0x3fffffffffffffffll 9 int belong[N],n,len,num[L],sta[L][L]; 10 //#define int long long 11 LL a[N],a0[L],d[L],upg[L]; 12 char B[1<<15],*S=B,*T=B; 13 #define getc (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?0:*S++) 14 inline int read() 15 { 16 int x=0,f=1;register char c=getc; 17 while(c<'0'||c>'9'){if(c=='-')f=-1;c=getc;} 18 while(c>='0'&&c<='9')x=10*x+(c^48),c=getc; 19 return x*f; 20 } 21 inline LL max(LL a,LL b){return a>b?a:b;} 22 inline int min(int a,int b){return a<b?a:b;} 23 inline LL calc(int id) 24 { 25 if(id==0)return -INF; 26 return a[id]+a0[belong[id]]+d[belong[id]]*(id-len*(belong[id]-1)-1)+upg[belong[id]]; 27 } 28 inline void clear(int id) 29 { 30 for(int i=len*(id-1)+1;i<=min(n,len*id);++i)a[i]=calc(i); 31 a0[id]=d[id]=upg[id]=0; 32 } 33 inline LL chaji(LL a,LL b,LL c,LL d){return a*d-b*c;} 34 inline void build(int id) 35 { 36 register int i,top=0; 37 for(i=len*(id-1)+1;i<=min(n,len*id);++i) 38 { 39 while(top>1&&chaji(sta[id][top]-sta[id][top-1],calc(sta[id][top])-calc(sta[id][top-1]),i-sta[id][top],calc(i)-calc(sta[id][top]))>=0)--top; 40 sta[id][++top]=i; 41 } 42 num[id]=top; 43 } 44 inline LL query(int id) 45 { 46 int l=2,r=num[id],mi,ans=1; 47 while(l<=r) 48 { 49 mi=l+r>>1; 50 if(calc(sta[id][mi-1])<=calc(sta[id][mi])) 51 ans=mi,l=mi+1; 52 else r=mi-1; 53 } 54 return calc(sta[id][ans]); 55 } 56 inline void update(int l,int r,int val) 57 { 58 int ida=belong[l],idb=belong[r],i; 59 LL tmpa0=0; 60 if(ida==idb) 61 { 62 clear(ida); 63 for(i=l;i<=r;++i)tmpa0+=val,a[i]+=tmpa0; 64 build(ida); 65 return; 66 } 67 clear(ida); 68 for(i=l;i<=len*ida;++i)tmpa0+=val,a[i]+=tmpa0; 69 build(ida); 70 for(i=ida+1;i<idb;++i)a0[i]+=tmpa0+val,d[i]+=val,tmpa0+=(LL)len*val; 71 clear(idb); 72 for(i=len*(idb-1)+1;i<=r;++i)tmpa0+=val,a[i]+=tmpa0; 73 for(i=r+1;i<=min(n,len*idb);++i)a[i]+=tmpa0; 74 build(idb); 75 for(i=idb+1;i<=belong[n];++i)upg[i]+=tmpa0; 76 } 77 inline LL query(int l,int r) 78 { 79 LL ret=-INF; 80 int ida=belong[l],idb=belong[r],i; 81 if(ida==idb) 82 { 83 for(i=l;i<=r;++i)ret=max(ret,calc(i)); 84 return ret; 85 } 86 for(i=l;i<=len*ida;++i)ret=max(ret,calc(i)); 87 for(i=ida+1;i<idb;++i)ret=max(ret,query(i)); 88 for(i=len*(idb-1)+1;i<=r;++i)ret=max(ret,calc(i)); 89 return ret; 90 } 91 signed main() 92 { 93 //freopen("Ark.in","r",stdin); 94 register int i,opt,m,l,r,v; 95 n=read(),len=sqrt(n+0.5); 96 for(i=1;i<=n;++i)a[i]=read()+a[i-1],belong[i]=(i-1)/len+1; 97 for(i=1;i<=belong[n];++i)build(i); 98 m=read(); 99 while(m--) 100 { 101 opt=read(),l=read(),r=read(); 102 if(opt)printf("%lld\n",query(l,r)); 103 else v=read(),update(l,r,v); 104 } 105 }
然后还有一道题目
这道题是高中对勾函数23333
我们可以简单的写一些式子啥的……然后我们可以计算出每个点的最优斜率是多少……
至于怎么算选手可以自己推一下,挺简单的……
然后我们会拿一堆斜率去尝试……这时候我们可以利用一个上凸壳来进行决策
从这个上凸壳的右侧往左跑,每一个点到下一个点之间都会有一个斜率区间,这一个斜率区间的最优决策点都是当前点
然后我们可以判断这个点的最优斜率是否在这个区间里面,然后进行一些判断即可
说起来挺简单的……实现的时候还是恶心了我一下的
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <cmath> 4 #include <algorithm> 5 using namespace std; 6 char B[1<<15],*S=B,*T=B; 7 #define getc (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?0:*S++) 8 inline int read() 9 { 10 int x=0;register char c=getc; 11 while(c<'0'||c>'9')c=getc; 12 while(c>='0'&&c<='9')x=10*x+(c^48),c=getc; 13 return x; 14 } 15 #define N 1000010 16 #define inf 0x7fffffff 17 #define INF 0x7fffffffffffffffll 18 #define db double 19 #define LL long long 20 struct Vector 21 { 22 int x,y;db k; 23 Vector(int a=0,int b=0){x=a,y=b;k=((x!=0)?((db)y/x):(-inf));} 24 inline Vector operator + (const Vector &b) const{return Vector(x+b.x,y+b.y);} 25 inline Vector operator - (const Vector &b) const{return Vector(x-b.x,y-b.y);} 26 inline LL operator * (const Vector &b) const{return (LL)x*b.y-(LL)y*b.x;} 27 }pt[N],ans[N]; 28 inline bool mt1(const Vector &a,const Vector &b) 29 { 30 return a.x==b.x?a.y<b.y:a.x>b.x; 31 } 32 33 int main() 34 { 35 // freopen("Ark.in","r",stdin); 36 register int i,n,top;n=read(); 37 for(i=1;i<=n;++i) 38 pt[i].x=read(),pt[i].y=read(); 39 sort(pt+1,pt+n+1,mt1); 40 for(i=1,top=0;i<=n;++i) 41 { 42 while(top>1&&(ans[top]-ans[top-1])*(pt[i]-ans[top])<0)--top; 43 ans[++top]=pt[i]; 44 } 45 ans[top+1]=Vector(0,ans[top].y); 46 db l=-INF,r,k2,sum=INF;Vector tmp; 47 for(i=1;i<=top;++i) 48 { 49 r=l,l=(ans[i+1]-ans[i]).k; 50 k2=-sqrt((db)ans[i].y/ans[i].x); 51 if(l>=k2&&k2>=r) 52 sum=min(sum,ans[i].x+ans[i].y-ans[i].y/k2-ans[i].x*k2); 53 else sum=min(sum,ans[i].x+ans[i].y-ans[i].y/l-ans[i].x*l); 54 if(l>0)break; 55 } 56 printf("%.4f\n",sum); 57 }
然后我们来看一下动态的凸包……这种凸包大概有2种,一种是比较友好的只带插入的凸包,一种是又插入又删除的凸包
只带插入的我们可以用一个平衡树维护……按x坐标建树,然后存个子树最前点和最后点,然后在插入一个新点之后弹掉两边的点
弹法和静态的一样,只不过我们加了个平衡树而已
然后我们看个板子题好了
没有什么好解释的,直接打就是了,我用的无旋Treap
当然我第一次打的代码比较蠢蛋……抄代码不要抄我的……
代码:
1 #include <cmath> 2 #include <cstdio> 3 #include <cstring> 4 #include <cstdlib> 5 #include <iostream> 6 using namespace std; 7 #define N 100010 8 #define eps 1e-6 9 char B[1<<15],*S=B,*T=B; 10 #define getc (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?0:*S++) 11 inline int read() 12 { 13 int x=0;register char c=getc; 14 while(c<'0'||c>'9')c=getc; 15 while(c>='0'&&c<='9')x=10*x+(c^48),c=getc; 16 return x; 17 } 18 #define db double 19 int n,m,q,cnt,nx,ny; 20 struct point 21 { 22 db x,y; 23 point(db a=0,db b=0){x=a,y=b;} 24 }pt[N]; 25 inline int sign(db a){return (a>-eps)-(a<eps);} 26 #define inf 0x7fffffff 27 inline db k(point ida,point idb) 28 { 29 if(sign(ida.x-idb.x)==0) 30 return ida.y>idb.y?-inf:inf; 31 return (ida.y-idb.y)/(ida.x-idb.x); 32 } 33 inline db sqr(db a){return a*a;} 34 inline db dis(point a,point b){return sqrt(sqr(a.x-b.x)+sqr(a.y-b.y));} 35 struct node 36 { 37 int key,size; 38 node *ch[2];point x; 39 node(){key=rand();} 40 inline void update(){size=ch[0]->size+1+ch[1]->size;} 41 }*null=new node(),*root,mem[N]; 42 int opt[N<<1],id[N<<1],tot,top; 43 db ans[N<<1],sum;bool vis[N]; 44 inline node* newnode(point x) 45 { 46 node *o=mem+(tot++);o->size=1; 47 o->ch[0]=o->ch[1]=null;o->x=x; 48 return o; 49 } 50 inline node* merge(node *a,node *b) 51 { 52 if(a==null)return b; 53 if(b==null)return a; 54 if(a->key>b->key){a->ch[1]=merge(a->ch[1],b),a->update();return a;} 55 else{b->ch[0]=merge(a,b->ch[0]),b->update();return b;} 56 } 57 #define D pair<node*,node*> 58 inline D split(node *o,int k) 59 { 60 if(o==null)return D(null,null);D y; 61 if(o->ch[0]->size>=k)y=split(o->ch[0],k),o->ch[0]=y.second,o->update(),y.second=o; 62 else y=split(o->ch[1],k-o->ch[0]->size-1),o->ch[1]=y.first,o->update(),y.first=o; 63 return y; 64 } 65 inline bool comp(point a,point b) 66 {return sign(a.x-b.x)==0?a.y<b.y:a.x<b.x;} 67 inline int get_rank(node *o,point a) 68 { 69 if(o==null)return 0; 70 return comp(a,o->x)?get_rank(o->ch[0],a):(get_rank(o->ch[1],a)+o->ch[0]->size+1); 71 } 72 inline node* get_end(node *o) 73 {while(o->ch[1]!=null)o=o->ch[1];return o;} 74 inline node* get_start(node *o) 75 {while(o->ch[0]!=null)o=o->ch[0];return o;} 76 inline void insert(point a) 77 { 78 int rk=get_rank(root,a),sz=root->size; 79 D tmp=split(root,rk),x1=D(null,null),x2=D(null,null); 80 node *x=newnode(a),*y,*tmp1=get_end(tmp.first),*tmp2=get_start(tmp.second); 81 if(k(tmp1->x,tmp2->x)>k(tmp1->x,a)){root=merge(tmp.first,tmp.second);return;} 82 db temp=dis(tmp1->x,tmp2->x); 83 if(rk>0&&rk<sz)sum-=temp; 84 x2=split(tmp.second,1); 85 while(x2.second!=null) 86 { 87 y=get_start(x2.second); 88 if(sign( k(a,x2.first->x) - k(x2.first->x,y->x) )<0) 89 sum-=dis(x2.first->x,y->x),x2=split(x2.second,1); 90 else break; 91 } 92 x1=split(tmp.first,--rk); 93 while(x1.first!=null) 94 { 95 y=get_end(x1.first); 96 if(sign(k(y->x,x1.second->x)-k(x1.second->x,a))<0) 97 sum-=dis(y->x,x1.second->x),x1=split(x1.first,--rk); 98 else break; 99 } 100 if(x1.second!=null) 101 sum+=dis(x1.second->x,a); 102 if(x2.first!=null) 103 sum+=dis(a,x2.first->x); 104 root=merge(merge(merge(x1.first,x1.second),x),merge(x2.first,x2.second)); 105 } 106 int main() 107 { 108 register int i; 109 n=read(),nx=read(),ny=read(),cnt=m=read(); 110 for(i=1;i<=m;++i)pt[i].x=read(),pt[i].y=read(); 111 q=read(); 112 for(i=1;i<=q;++i) 113 {opt[i]=read();if(opt[i]==1)id[i]=read(),vis[id[i]]=1;} 114 null->ch[0]=null->ch[1]=null;null->size=0; 115 116 root=newnode(point(nx,ny)), 117 root->ch[0]=newnode(point(0,0)), 118 root->ch[1]=newnode(point(n,0)); 119 root->update(); 120 sum=dis(point(0,0),point(nx,ny))+dis(point(n,0),point(nx,ny)); 121 for(i=1;i<=m;++i) 122 if(!vis[i])insert(pt[i]); 123 for(i=q;i;--i) 124 if(opt[i]==1)insert(pt[id[i]]); 125 else ans[++top]=sum; 126 for(i=top;i;--i)printf("%.2f\n",ans[i]); 127 }
然后如果要支持插入删除的……我们有两种选择,一个是线段树分治
说的玄乎……只不过在分治的每一个单元存储了一些信息
我们从树根走到叶子,遇到的信息就是我们想要的全部信息
那么这个具体的题的话,我们可以发现这个点积是和投影有关的
所以我们可以三分凸壳,这个凸壳上最接近它的点就是最优点
我们沿用上面陶陶的难题那道题的思路,在log个可决策点里面选最优的
这种逐步逼近的思想很优秀,不少题都用了这种思想
也即不一次搞到最优解,而是通过若干次询问得到最终答案
那么代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <cmath> 5 using namespace std; 6 #define inf 0x7fffffffll 7 #define INF 0x7fffffffffffffffll 8 #define N 400010 9 #define LL long long 10 #define sqr(a) ((LL)(a)*(a)) 11 struct Vector 12 { 13 int x,y; 14 Vector(int a=0,int b=0){x=a,y=b;} 15 inline Vector operator + (const Vector &b)const {return Vector(x+b.x,y+b.y);} 16 inline Vector operator - (const Vector &b)const {return Vector(x-b.x,y-b.y);} 17 inline LL operator * (const Vector &b) const {return (LL)x*b.y-(LL)y*b.x;} 18 }vec[N]; 19 inline LL Dot (const Vector &a,const Vector &b) {return (LL)a.x*b.x+(LL)a.y*b.y;} 20 int Mem[N<<5],*head=Mem,Mem2[N<<5],*head2=Mem2,Mem3[N<<5],*head3=Mem3; 21 struct node 22 { 23 node *ch[2]; 24 int *sta,*ans1,*ans2,sz1,sz2; 25 }*root=NULL,mem[N<<1];int tot; 26 inline bool mt(const int &a,const int &b) 27 {return vec[a].x==vec[b].x?vec[a].y<vec[b].y:vec[a].x<vec[b].x;} 28 inline void insert(node *&o,int l,int r,const int &pos) 29 { 30 if(o==NULL) 31 o=mem+(tot++),o->ch[0]=o->ch[1]=NULL, 32 o->sta=head,o->ans1=head2,o->ans2=head3, 33 head+=r-l+1,head2+=r-l+1,head3+=r-l+1; 34 o->sta[pos-l]=pos; 35 if(pos==r) 36 { 37 int top=-1,len=r-l+1,i; 38 sort(o->sta,o->sta+len,mt); 39 for(top=-1,i=0;i<len;++i) 40 { 41 while(top>0&&(vec[o->ans1[top]]-vec[o->ans1[top-1]])*(vec[o->sta[i]]-vec[o->ans1[top]])>0)--top; 42 o->ans1[++top]=o->sta[i]; 43 }o->sz1=top+1; 44 for(top=-1,i=0;i<len;++i) 45 { 46 while(top>0&&(vec[o->ans2[top]]-vec[o->ans2[top-1]])*(vec[o->sta[i]]-vec[o->ans2[top]])<0)--top; 47 o->ans2[++top]=o->sta[i]; 48 }o->sz2=top+1; 49 } 50 if(l==r)return; 51 register int mi=(l+r)>>1; 52 if(pos<=mi)insert(o->ch[0],l,mi,pos); 53 else insert(o->ch[1],mi+1,r,pos); 54 } 55 inline LL max(const LL &a,const LL &b){return a>b?a:b;} 56 inline LL calc(const node *o,const int opt,const Vector q) 57 { 58 LL ret=-INF; 59 if(opt==1) 60 { 61 int l=0,r=o->sz1-1,ll,rr; 62 while(l+3<=r) 63 { 64 ll=(l+l+r)/3,rr=(l+r+r)/3; 65 if(Dot(vec[o->ans1[ll]],q)>Dot(vec[o->ans1[rr]],q))r=rr; 66 else l=ll; 67 } 68 for(int i=l;i<=r;++i)ret=max(ret,Dot(vec[o->ans1[i]],q)); 69 return ret; 70 } 71 else 72 { 73 int l=0,r=o->sz2-1,ll,rr; 74 while(l+3<=r) 75 { 76 ll=(l+l+r)/3,rr=(l+r+r)/3; 77 if(Dot(vec[o->ans2[ll]],q)>Dot(vec[o->ans2[rr]],q))r=rr; 78 else l=ll; 79 } 80 for(int i=l;i<=r;++i)ret=max(ret,Dot(vec[o->ans2[i]],q)); 81 return ret; 82 } 83 } 84 inline LL query(const node *o,int l,int r,const int &L,const int &R,const Vector &q) 85 { 86 if(L<=l&&r<=R)return q.y>0?calc(o,1,q):calc(o,2,q); 87 register int mi=(l+r)>>1;LL ret=-INF; 88 if(L<=mi)ret=query(o->ch[0],l,mi,L,R,q); 89 if(mi<R)ret=max(ret,query(o->ch[1],mi+1,r,L,R,q)); 90 return ret; 91 } 92 int main() 93 { 94 // freopen("Ark.in","r",stdin); 95 register int n,i,a,b,l,r,isnot,cnt=0;char opt[3];LL ans=0; 96 scanf("%d%s",&n,opt); 97 isnot=(opt[0]!='E'); 98 for(i=1;i<=n;++i) 99 { 100 scanf("%s%d%d",opt,&a,&b); 101 if(isnot)a^=(ans&inf),b^=(ans&inf); 102 if(opt[0]=='A') 103 vec[++cnt]=Vector(a,b),insert(root,1,n,cnt); 104 else 105 { 106 scanf("%d%d",&l,&r); 107 if(isnot)l^=(ans&inf),r^=(ans&inf); 108 printf("%lld\n",ans=query(root,1,n,l,r,Vector(a,b))); 109 } 110 } 111 }
至于另外一种,是用可持久化平衡树来维护区间的凸壳
然后用一个数据结构套起来……
在更新的时候我们可以找两个相邻凸壳的公切线,然后合并之,复杂度$logn$
说着简单……打起来很麻烦的
分类讨论贼多
打了一道NOI2017影分身,被uoj的特殊hack数据日死了
由于是猫树维护,所以似乎点集是一条直线
但是官方数据的20个都过了……
1 #pragma GCC optimize("O3") 2 #include <cstdio> 3 #include <ctime> 4 #include <cstring> 5 #include <cstdlib> 6 #include <iostream> 7 #include <algorithm> 8 using namespace std; 9 #define LL long long 10 #define inf 100000001 11 #define db double 12 #define N 100010 13 char B[1<<20],*cS=B,*cT=B; 14 #define getc (cS==cT&&(cT=(cS=B)+fread(B,1,1<<20,stdin),cS==cT)?0:*cS++) 15 inline int read() 16 { 17 int x=0,f=1;register char c=getc; 18 while(c<'0'||c>'9'){if(c=='-')f=-1;c=getc;} 19 while(c>='0'&&c<='9')x=10*x+(c^48),c=getc; 20 return x*f; 21 } 22 char O[1<<22],*cI=O; 23 inline void write(LL x) 24 { 25 if(!x)*cI++='0'; 26 else 27 { 28 LL y;static char c[20], *i = c; 29 while (x)y=x/10,*i++=x-y*10+'0',x=y; 30 while (i != c)*cI++=*--i; 31 } 32 *cI++='\n'; 33 } 34 struct Vector 35 { 36 int x,y; 37 Vector(int a=0,int b=0){x=a,y=b;} 38 inline Vector operator + (const Vector &a){return Vector(x+a.x,y+a.y);} 39 inline Vector operator - (const Vector &a){return Vector(x-a.x,y-a.y);} 40 inline LL operator * (const Vector &a){return (LL)x*a.y-(LL)y*a.x;} 41 }p[N*2];int cnt; 42 struct node 43 { 44 int key,lc,rc,it; 45 node *ch[2];LL sum; 46 node(){key=rand();} 47 inline void update() 48 { 49 sum=ch[0]->sum+ch[1]->sum; 50 lc=ch[0]->lc?ch[0]->lc:it; 51 if(ch[0]->rc)sum+=p[it]*p[ch[0]->rc]; 52 rc=ch[1]->rc?ch[1]->rc:it; 53 if(ch[1]->lc)sum+=p[ch[1]->lc]*p[it]; 54 } 55 }*null,mem[N<<7];int tot; 56 inline node* newnode(int id) 57 { 58 node *o=mem+(tot++);o->ch[0]=o->ch[1]=null; 59 o->lc=o->rc=o->it=id,o->sum=0;return o; 60 } 61 #define cop(a,b) ( ((b)==null)?(a=null):a=newnode(0),*a=*b ) 62 inline node* merge(node *a,node *b) 63 { 64 node *rt; 65 if(a==null)return b; 66 else if(b==null)return a; 67 else if(a->key>b->key) 68 cop(rt,a),rt->ch[1]=merge(rt->ch[1],b),rt->update(); 69 else 70 cop(rt,b),rt->ch[0]=merge(a,rt->ch[0]),rt->update(); 71 return rt; 72 } 73 #define D pair<node*,node*> 74 db mid; 75 inline db cross(Vector a,Vector b,Vector c,Vector d) 76 { 77 db la=(b-d)*(c-d),lb=(c-d)*(a-d); 78 return (a.x+lb/(lb+la)*(b.x-a.x)); 79 } 80 inline D join(node *x,node *y) 81 { 82 int a=x->it,b=y->it,la=x->ch[0]->rc,ra=x->ch[1]->lc,lb=y->ch[0]->rc,rb=y->ch[1]->lc; 83 D ret;node *o; 84 if(ra&&lb&&(p[ra]-p[b])*(p[a]-p[b])>=0&&(p[b]-p[a])*(p[lb]-p[a])>=0) 85 { 86 db mi=cross(p[a],p[ra],p[lb],p[b]); 87 if(mi<mid) 88 { 89 cop(o,x),ret=join(o->ch[1],y); 90 o->ch[1]=ret.first,o->update(),ret.first=o; 91 return ret; 92 } 93 else 94 { 95 cop(o,y),ret=join(x,o->ch[0]); 96 o->ch[0]=ret.second,o->update(),ret.second=o; 97 return ret; 98 } 99 } 100 if(la&&(p[a]-p[la])*(p[b]-p[a])>=0)return join(x->ch[0],y); 101 if(rb&&(p[b]-p[a])*(p[rb]-p[b])>=0)return join(x,y->ch[1]); 102 if(ra&&(p[ra]-p[b])*(p[a]-p[b])>=0) 103 { 104 cop(o,x),ret=join(o->ch[1],y); 105 o->ch[1]=ret.first,o->update(),ret.first=o; 106 return ret; 107 } 108 if(lb&&(p[b]-p[a])*(p[lb]-p[a])>=0) 109 { 110 cop(o,y),ret=join(x,o->ch[0]); 111 o->ch[0]=ret.second,o->update(),ret.second=o; 112 return ret; 113 } 114 // { 115 node *oo; 116 if(x->ch[1]==null)o=x; 117 else cop(o,x),o->ch[1]=null,o->update(); 118 if(y->ch[0]==null)oo=y; 119 else cop(oo,y),oo->ch[0]=null,oo->update(); 120 return D(o,oo); 121 // } 122 } 123 inline node* solve(node *a,node *b) 124 { 125 if(a==null)return b; 126 if(b==null)return a; 127 mid=(p[a->rc].x+p[b->lc].x)*0.5; 128 D x=join(a,b); 129 return merge(x.first,x.second); 130 } 131 inline void init() 132 { 133 null=new node();null->ch[0]=null->ch[1]=null; 134 null->sum=0;null->lc=null->rc=null->it=0; 135 } 136 int n,m,Tot,aski[110],tmp[110]; 137 struct tree{tree *ch[2];node **sta;}Mem[N<<2]; 138 inline bool mt(const int &a,const int &b) 139 {return p[a].x==p[b].x?p[a].y<p[b].y:p[a].x<p[b].x;} 140 // inline void dfs(node *o) 141 // { 142 // if(o->ch[0]!=null)dfs(o->ch[0]); 143 // printf("%d(%d,%d)\n",o->it,p[o->it].x,p[o->it].y ); 144 // if(o->ch[1]!=null)dfs(o->ch[1]); 145 // } 146 struct Segment_Tree 147 { 148 tree *root; 149 node *lv[N],**head,*la[N*18],*T; 150 int id[N],rk[N]; 151 inline tree* build(int l,int r) 152 { 153 tree *o=Mem+(Tot++); 154 o->sta=head,head+=r-l+1; 155 if(l==r){o->sta[0]=lv[l];return o;} 156 register int i,mi=(l+r)>>1; 157 o->ch[0]=build(l,mi),o->ch[1]=build(mi+1,r); 158 o->sta[mi-l]=lv[mi],o->sta[mi+1-l]=lv[mi+1]; 159 for(i=mi-1;i>=l;--i)o->sta[i-l]=solve(lv[i],o->sta[i-l+1]); 160 for(i=mi+2;i<=r;++i)o->sta[i-l]=solve(o->sta[i-l-1],lv[i]); 161 // printf("%d--%d\n",l,r ); 162 // for(i=l;i<=r;++i) 163 // dfs(o->sta[i-l]),printf("\n\n\n\n"); 164 return o; 165 } 166 inline void build(int op) 167 { 168 register int i;head=la; 169 for(i=1;i<=n;++i)id[i]=op+i; 170 sort(id+1,id+n+1,mt); 171 for(i=1;i<=n;++i)rk[id[i]-op]=i; 172 for(i=1;i<=n;++i)lv[i]=newnode(id[i]); 173 root=build(1,n); 174 } 175 inline void query(tree *o,int l,int r,int L,int R) 176 { 177 register int mi=(l+r)>>1; 178 if(L<=mi&&mi<=R) 179 T=solve(T,o->sta[L-l]),T=solve(T,o->sta[R-l]); 180 else if(R<=mi)query(o->ch[0],l,mi,L,R); 181 else query(o->ch[1],mi+1,r,L,R); 182 } 183 inline LL getans(int op,int ge,Vector &l,Vector &r) 184 { 185 register int i,last,temp=tot;T=null; 186 for(i=1;i<=ge;++i)tmp[i]=rk[aski[i]]; 187 sort(tmp+1,tmp+ge+1); 188 for(i=last=1;i<=ge;last=tmp[i]+1,++i) 189 if(tmp[i]>last)query(root,1,n,last,tmp[i]-1); 190 if(last<=n)query(root,1,n,last,n); 191 l=p[T->lc],r=p[T->rc]; 192 tot=temp; 193 return T->sum+l*r; 194 } 195 }root1,root2; 196 int main() 197 { 198 // freopen("Ark.in","r",stdin); 199 // freopen("phantom.in","r",stdin); 200 // freopen("phantom.out","w",stdout); 201 p[0].x=p[0].y=-inf,init(),n=read(),m=read(); 202 register int i,j,ge;LL ans=n-1; 203 for(i=1;i<=n;++i)p[i].x=read(),p[i].y=read(); 204 root1.build(0); 205 for(i=1;i<=n;++i)p[i+n].x=p[i].x,p[i+n].y=-p[i].y; 206 // printf("%d %d\n",tot,(N<<7)-tot ); 207 // return 0; 208 root2.build(n); 209 Vector upl,upr,downl,downr; 210 while(m--) 211 { 212 for(ge=read(),i=1;i<=ge;++i)aski[i]=(read()%n+ans)%n+1; 213 ans=root1.getans(0,ge,upl,upr)+root2.getans(n,ge,downl,downr); 214 downl.y*=-1,downr.y*=-1; 215 ans+=(downr-downl)*(upr-downl)+(upr-downl)*(upl-downl),write(ans); 216 } 217 fwrite(O,1,cI-O,stdout); 218 }
在这之后……然后我们可以看另外两道凸壳的问题,他们没有使用搞基数据结构,而是使用了cdq分治
其中一个我似乎写过题解了
另外一道题……也是一个斜率优化的题目,在这里写一下
推式子的过程我们可以略过了……那个dp还是很简单的,暴力dp是枚举每个祖先去更新自己
然后我们考虑dp优化,然后推一个斜率的式子
接着我们通过点分治来优化这个思想,因为它的形态结构很好……
大致步骤是这样的
1.找到重心
2.对重心的原树父亲联通块递归执行此操作
3.dfs重心的儿子,得到需要更新的点
4.然后我们把它们按照“能更新他们的最浅深度”按从大到小排序,这样就单调了
5.然后我们维护从当前重心往他的原树祖先的一个决策凸壳,用推出来的斜率式子更新
6.递归其余的儿子联通块们
大概就是这样的……当时我脑残了一波,排序的关键字搞错了
后来才想明白……其实这个是一个cdq思想的点分治啊233
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 char B[1<<15],*S=B,*T=B; 6 #define getc (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?0:*S++) 7 template <typename _T> 8 inline _T read() 9 { 10 _T x=0;register char c=getc; 11 while(c<'0'||c>'9')c=getc; 12 while(c>='0'&&c<='9')x=10*x+(c^48),c=getc; 13 return x; 14 } 15 #define LL long long 16 #define N 200010 17 #define inf 100000000000000000ll 18 int n,e,adj[N],fa[N],maxsize[N],size[N]; 19 LL limit[N],p[N],q[N],f[N],deep[N]; 20 struct edge{int zhong,next;LL val;bool ban;}s[N]; 21 inline void add(int qi,int zhong,LL val) 22 {s[++e].zhong=zhong;s[e].next=adj[qi];s[e].ban=0;s[e].val=val;adj[qi]=e;} 23 inline void dfs0(int rt) 24 { 25 for(int i=adj[rt];i;i=s[i].next) 26 deep[s[i].zhong]=deep[rt]+s[i].val,dfs0(s[i].zhong); 27 } 28 inline int max(int a,int b){return a>b?a:b;} 29 inline LL min(LL a,LL b){return a<b?a:b;} 30 inline void dfs1(int rt,int totsize,int &root) 31 { 32 size[rt]=1,maxsize[rt]=0; 33 for(int i=adj[rt];i;i=s[i].next)if(!s[i].ban) 34 dfs1(s[i].zhong,totsize,root),size[rt]+=size[s[i].zhong], 35 maxsize[rt]=max(maxsize[rt],size[s[i].zhong]); 36 maxsize[rt]=max(maxsize[rt],totsize-size[rt]); 37 if(maxsize[rt]<=maxsize[root])root=rt; 38 } 39 int sta[N],tot; 40 inline bool mt1(const int &a,const int &b) 41 {return deep[a]-limit[a]>deep[b]-limit[b];} 42 inline void dfs2(int rt) 43 { 44 sta[++tot]=rt; 45 for(int i=adj[rt];i;i=s[i].next) 46 if(!s[i].ban)dfs2(s[i].zhong); 47 } 48 int stack[N],top; 49 #define db double 50 inline double k(const int ida,const int idb) 51 {return (db)(f[ida]-f[idb])/(deep[ida]-deep[idb]);} 52 inline void insert(int id) 53 { 54 register int l=2,r=top,mi,ans=top+1; 55 while(l<=r) 56 { 57 mi=(l+r)>>1; 58 if( k( stack[mi],id ) > k( stack[mi],stack[mi-1] ) )ans=mi,r=mi-1; 59 else l=mi+1; 60 } 61 stack[top=ans]=id; 62 } 63 inline int query(int id) 64 { 65 if(top==1)return stack[1]; 66 register int l=1,r=top-1,mi,ans=r; 67 while(l<=r) 68 { 69 mi=(l+r)>>1; 70 if(k(stack[mi],stack[mi+1])<p[id])ans=mi,r=mi-1; 71 else l=mi+1; 72 } 73 return (k(stack[ans],stack[ans+1])<p[id])?stack[ans]:stack[ans+1]; 74 } 75 inline void solve(int rt,int siz) 76 { 77 if(siz<=1)return; 78 register int i,u,v,root=0,cur; 79 dfs1(rt,siz,root); 80 for(i=adj[root];i;i=s[i].next)s[i].ban=1; 81 solve(rt,siz-size[root]+1); 82 for(tot=0,i=adj[root];i;i=s[i].next)dfs2(s[i].zhong); 83 sort(sta+1,sta+tot+1,mt1); 84 for(cur=root,top=0,i=1;i<=tot;++i) 85 { 86 u=sta[i]; 87 while(cur!=fa[rt]&&deep[u]-deep[cur]<=limit[u]) 88 insert(cur),cur=fa[cur]; 89 v=query(u); 90 if( ( double)f[v]+p[u]*1.0*(deep[u]-deep[v])+q[u] <=inf )f[u]=min(f[u],f[v]+p[u]*(deep[u]-deep[v])+q[u]); 91 } 92 for(i=adj[root];i;i=s[i].next) 93 u=s[i].zhong,solve(u,size[u]); 94 } 95 int main() 96 { 97 register int i;LL tmp; 98 memset(f,0x7f,sizeof(f)),f[1]=0; 99 n=read<int>(),maxsize[0]=n+1; 100 read<int>(); 101 for(i=2;i<=n;++i) 102 fa[i]=read<int>(),tmp=read<LL>(),add(fa[i],i,tmp), 103 p[i]=read<LL>(),q[i]=read<LL>(),limit[i]=read<LL>(); 104 dfs0(1),solve(1,n); 105 for(i=2;i<=n;++i)printf("%lld\n",f[i]); 106 }
大概我这阶段做的凸壳专题就是这么多……
我们抛开纯计算几何不谈,凸包的主要运用是优化决策,把一些$O(n)$转化为$O(logn)$
这种优化决策的思想很不错……
看到分式的时候我们可以优先考虑斜率,凸壳以及01分数规划
就是这些……