凸包(壳)算法小结

凸包专题大概是我做的最吃shi考验代码能力的专题……

然后……大概我们的凸包可以分为静态凸包和动态凸包,从功能上可以分为决策性的凸包和计算几何性的凸包

其实没有多少区别,打就好了

静态凸包啥的我就不介绍怎么打了……

然后我推荐在弹栈的时候用叉积而不是暴力算斜率,那样讨论好多啊……

另外,一般我们不会被卡弹栈……但如果被卡时间的话,我们可以考虑二分弹栈,这样并没有什么问题……

然后我们来看看凸包可以干什么:

题目1:bzoj2402

然后我一开始想歪了……我想直接把(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 }
bzoj2402

还有比较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 }
bzoj2388

然后还有一道题目

这道题是高中对勾函数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 }
bzoj4570

然后我们来看一下动态的凸包……这种凸包大概有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 }
bzoj2300

然后如果要支持插入删除的……我们有两种选择,一个是线段树分治

说的玄乎……只不过在分治的每一个单元存储了一些信息

我们从树根走到叶子,遇到的信息就是我们想要的全部信息

我们看个题

那么这个具体的题的话,我们可以发现这个点积是和投影有关的

所以我们可以三分凸壳,这个凸壳上最接近它的点就是最优点

我们沿用上面陶陶的难题那道题的思路,在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 }
bzoj3533

至于另外一种,是用可持久化平衡树来维护区间的凸壳

然后用一个数据结构套起来……

在更新的时候我们可以找两个相邻凸壳的公切线,然后合并之,复杂度$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 }
uoj319

在这之后……然后我们可以看另外两道凸壳的问题,他们没有使用搞基数据结构,而是使用了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 }
bzoj3672

大概我这阶段做的凸壳专题就是这么多……

我们抛开纯计算几何不谈,凸包的主要运用是优化决策,把一些$O(n)$转化为$O(logn)$

这种优化决策的思想很不错……

看到分式的时候我们可以优先考虑斜率,凸壳以及01分数规划

就是这些……

posted @ 2018-01-19 19:45 LadyLex 阅读(...) 评论(...) 编辑 收藏