zoukankan      html  css  js  c++  java
  • 凸包(壳)算法小结

    凸包专题大概是我做的最吃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
    ",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
    ",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
    ",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
    ",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
    ",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++='
    ';
     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)
    ",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
    ",l,r );
    162         // for(i=l;i<=r;++i)
    163         //     dfs(o->sta[i-l]),printf("
    
    
    
    ");
    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
    ",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
    ",f[i]);
    106 }
    bzoj3672

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

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

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

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

    就是这些……

  • 相关阅读:
    vue小结
    ES6中的super关键字
    es5和es6
    雅虎工程师提供的CSS初始化示例代码
    移动端rem用法总结
    批量压缩图片
    cordova
    cordova 添加插件时报错相关问题
    JS 数组中对象去重 reduce 用法
    中间件笔录
  • 原文地址:https://www.cnblogs.com/LadyLex/p/8310935.html
Copyright © 2011-2022 走看看