• 「学习笔记」计算几何杂论


    向量

    • 坐标表示 ((x,y))

    • 叉乘:((n,m))((x,y)) 的向量叉积的结果为 (ny-mx),利用这种方法可以判断两个向量的方向

      也就是如果 mul(Vector x,Vector y)>=0 那么 (x)(y) 的逆时针

      如果为负也就是在顺时针,有一个小口诀:顺负逆正

      这里注意:叉积可能会爆 int

    凸包

    概念: 「HAOI2011」防线修建

    根据题目的图示,可以得到如果一个上凸壳两点间的斜率是单调递减的

    类似的,下凸壳是斜率递增的

    那么这题目离线之后用 (set) 维护凸包周长即可

    凸包与斜率优化:

    (dp) 式子的转移的经典优化有斜率优化,单调队列处理的斜率优化条件比较苛刻,需要两边都满足单调

    本题中并不满足 (l_i) 有任何单调性,那么考虑动态维护一个凸包

    这类题目有很多种解法,也就引出了多种维护凸包的方式:

    • 「NOI2014」购票

      用线段树来维护凸包

      对于线段树上的每个区间维护区间里面的点的凸壳,每次对 (log) 个节点插入

      考虑到每个节点每个点只会入队和出队一次,那么复杂度是 (1)(log)

      但是在这个题目中考虑转移顺序的问题,要先处理 (out) 序,每个点先查得到答案之后在 (out[x]) 处加入新点

    • 「BZOJ2402」陶陶的难题

      (0/1) 分数规划转化问题之后得到维护 (y-mid imes x+q-mid imes p) 的式子

      因为分别独立,那么把 (x,y)(p,q) 分开扔到两个线段树里面

      同时需要树链剖分,那么复杂度达到惊人的 (log ^4)

      其实没啥,因为后面三个根本跑不满,线段树无法满 (log) ,vector里面的二分也是不行的,重链剖分更是小常数

    • 「SDOI2014」向量集

      首先由点乘的公式能得到斜率优化的式子,这是 (trivial)

      那么套上线段树维护凸包就行了

      不过这题目中答案的来源不仅是 ((x,y)) 来构成,也可以是一个下凸壳

      那么对于每个加入向量集里面的点维护 ((x,y))((-x,-y)) 即可

      同时对于每个线段树上的点,(vector) 的大小等于区间大小再按照 (x) 归并

      记得要清空……

    • 「NOI2009」货币兑换

      平衡树维护凸包或者 (cdq) 分治来维护

      平衡树维护就是考虑凸包的性质,每次需要修改前趋和后继,对应上去就是 (pre,nxt)

      对于具体实现而言,维护 (l[x],r[x]) 表示当前点左边和右边的斜率,删点的时候细节比较多

      inline int find(int x,double num) 
      {
          if(!x) return 0;
          if(l[x]+eps>=num&&r[x]<=num+eps) return x;
          if(l[x]<num+eps) return find(ls[x],num);
          return find(rs[x],num);    
      }  
      inline double calc(int p,int q)
      {
          if(fabs(x[p]-x[q])<eps) return -inf;
          return (y[p]-y[q])/(x[p]-x[q]);
      }
      inline int pre(int x)
      {
          int now=ls[x],res=now; 
          while(now)
          {
              if(l[now]+eps>=calc(now,x)) res=now,now=rs[now];
              else now=ls[now];
          } return res;
      }
      inline int nxt(int x) 
      {   
          int now=rs[x],res=now;
          while(now) 
          {
              if(r[now]<=eps+calc(now,x)) res=now,now=ls[now];
              else now=rs[now]; 
          } return res;
      }
      inline void del(int x)
      {
          rt=ls[x]; fa[rt]=0; fa[rs[x]]=rt; rs[rt]=rs[x]; 
          r[rt]=l[rs[x]]=calc(rt,rs[x]);
          return ; 
      }
      inline void work(int x)
      {
          splay(x,0);
          if(ls[x])
          {
              int p=pre(x); splay(p,x); rs[p]=0;
              r[ls[x]]=l[x]=calc(ls[x],x);
          }else l[x]=inf;
          if(rs[x])
          {
              int tmp=nxt(x); splay(tmp,x); ls[tmp]=0;
              l[rs[x]]=r[x]=calc(rs[x],x);
          }else r[x]=-inf;
          if(l[x]<=eps+r[x]) del(x);
          return ;
      }
      inline void insert(int &now,int fat,int id)
      {
          if(!now){now=id; fa[now]=fat; return ;}
          if(x[id]<=x[now]+eps) insert(ls[now],now,id); else insert(rs[now],now,id);
          return ;
      }
      

      (cdq) 分治就是一个经典的左中右转移

      对于当前分治区间 ([l,r]),先处理左边的贡献,把左边按照 (dp) 建立凸包,再做右边

      代码没有写,因为这种题貌似是没少做

      复杂度貌似并不比上面的做法多 (log),但是不能在线

    应用

    其实有些题目就是转化出来是凸包的模型,然后用二分斜率的方式处理

    • Luogu4192 旅行规划

    区间问题考虑初等数据结构,发现线段树并不能维护之后考虑分块

    那么可以给每个块建一个凸包,(i-st+1) 是横坐标,(sum_i) 是纵坐标

    对于修改操作,小块重构,大块加等差数列,打标记维护公差和首项

    注意在 (r) 以后的点的 (sum) 应该增加 (len imes (r-l+1))

    查询操作,小块暴力修改并且重构,大块对着 (-d) 二分就行


    上面提到的凸包二分是比较恶心的东西,不好写对:

    一种可行的写法是

    l=0,r=st[id].size()-2;
    while(l<=r) check->slope r=mid-1; else l=mid+1;
    ans=l;
    

    这个对于上下凸壳需要改一些条件

    做的题目并不够多,所以本文会持续更新

  • 相关阅读:
    ( 三十一 )、 SpringBoot项目启动自动执行sql脚本
    转:OAuth 2.0 介绍
    采坑:python base64
    Redis学习——数据结构介绍(四)
    美化你的博客园,flash插件的使用
    Redis学习——详解Redis配置文件(三)
    Redis学习——Windows环境下Redis的安装(二)
    Redis学习——Linux环境下Redis的安装(一)
    NPM 安装速度慢,镜像修改
    日志配置(springboot、mybatis、Lombok)
  • 原文地址:https://www.cnblogs.com/yspm/p/14379744.html
Copyright © 2020-2023  润新知