李超线段树(Li-Chao Segment Tree)

李超线段树

李超线段树是一种用于维护平面直角坐标系内线段关系的数据结构。它常被用来处理这样一种形式的问题:给定一个平面直角坐标系,支持动态插入一条线段,询问从某一个位置 ( X , + ∞ ) (X,+\infty) (X,+) 向下看能看到的最高的一条线段(也就是给一条竖线,问这条竖线与所有线段的最高的交点
在这里插入图片描述


如上图,有三条线段,两条红色竖线代表两个询问,则点 A A A 与点 B B B 就是询问到的答案。
李超线段树的核心是维护每个区间的“最优势线段”,即在每个区间的中点处最高的线段。询问时我们可以对所有包含横坐标为 x x x 的位置的区间上的最优势线段计算答案,最后取个 m a x max max
其实这就相当于维护一个记录当前区间最高线段的,不下传标记的线段树。(显然如果我们访问到的区间内只包含询问的横坐标,那么这个区间的最优势线段就是答案线段,所以这样统计包含答案是能保证正确性的)


在这里插入图片描述


如上图,对于区间 [ 0 , 8 ] [0,8] [0,8] ,绿色线段是“最优势线段”

对于修改,我们先把线段的值域分割到线段树的区间上,每次访问一个完整的包含在线段值域中的区间时:

  1. 若当前区间还没有记录最优势线段,则记录最优势线段并返回。
  2. 若当前区间的最优势线段被插入的线段完全覆盖,则把最优势线段修改为被插入线段并返回。
  3. 若当前区间的最优势线段把被插入线断完全覆盖,则直接返回。
  4. 若当前区间最优势线段与被插入线段有交,则先判断哪条线段在当前区间更优,并把更劣的线段下传到交点所在子区间。(交点两边的部分被这两条线段分别控制,而我们已经让在中点更优的那条线段作为区间最优势线段,因此更劣的那条线段只有可能在交点所在子区间超过当前区间的最优势线段)

代码块:

struct Line {
    double k, b;//y = kx + b;
    int l, r, flag, idx;//线段树的l 和 r 以及是否有线段覆盖 以及插入线段的编号
    Line(){}//无参构造函数
    //有参构造函数
    Line(double k, double b, int l, int r, int flag, int idx) {
        this->k = k, this->b = b, this->l = l, this->r = r, this->flag = flag, this->idx = idx;
    }
    //算在pos位置得y值是多少
    double calc(int pos) {return k * pos + b;}//直线里面pos位置的y值
    //线段交点的x坐标
    int corss(const Line & rhs) const {return floor((b - rhs.b) / (rhs.k - k));}
}seg[maxn << 1];

inline void update(int cutl, int cutr, int rt,Line rhs) {
//如果当前区间以及被目标区间覆盖
    if(rhs.l <= cutl && rhs.r >= cutr) {
      //如果还没有线段覆盖
        if(!seg[rt].flag) {seg[rt] = rhs,seg[rt].flag = 1;}
      //如果两端都比原线段高就直接更新
        else if(rhs.calc(cutl) - seg[rt].calc(cutl) > eps && rhs.calc(cutr) - seg[rt].calc(cutr) > eps) seg[rt] = rhs;
       //如果两线段相交
        else if(rhs.calc(cutl) - seg[rt].calc(cutl) > eps || rhs.calc(cutr) - seg[rt].calc(cutr) > eps) {
        //取中点
            int Mid = (cutl + cutr) >> 1;
        //判断两个线段中点的位置的y值大小
        //交换
            if(rhs.calc(Mid) - seg[rt].calc(Mid) > eps) swap(rhs,seg[rt]);
            if(rhs.corss(seg[rt]) - Mid < -eps) update(cutl, Mid, rt << 1, rhs);
            else update(Mid + 1, cutr, rt << 1|1,rhs);
        }
        
    } else {//线段树常规套路
        int Mid = (cutl + cutr) >> 1;
        if(rhs.l <= Mid) update(cutl,Mid, rt << 1,rhs);
        if(rhs.r > Mid) update(Mid + 1, cutr, rt << 1|1, rhs);
    }
}


inline void ask(int rt, int l, int r, int x) {
      //每一层都要跑一个询问
      double tmp = seg[rt].calc(x);
      if(tmp - h > eps) lastans = seg[rt].idx, h = tmp;
      else if(fabs(h - tmp) < eps) lastans = min(lastans,seg[rt].idx); 
      if(l == r) return;
      if(x <= mid) ask(Lson,x);
      if(x > mid) ask(Rson,x);
}

#include<bits/stdc++.h>
#define mid ((l + r) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define _for(i,a,b) for( int i = (a); i < (b); ++i)
#define _rep(i,a,b) for( int i = (a); i <= (b); ++i)
#define for_(i,a,b) for( int i = (a); i >= (b); -- i)
#define rep_(i,a,b) for( int i = (a); i > (b); -- i)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define LLF 0x3f3f3f3f3f3f3f3f
#define hash Hash
#define next Next
#define pb push_back
#define f first
#define s second
#define y1 Y
using namespace std;
const int N = 1e7 + 10, modx = 39989, mody = 1e9;
const int maxn = 4e5 + 10;
const long double eps = 0.0;
const int EPS = 500 * 500;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef pair<double,double> PDD;
const int BUF=30000000;
char Buf[BUF],*buf=Buf;
template<typename T> void read(T &a)
{
    for(a=0;*buf<48;buf++);
    while(*buf>47) a=a*10+ *buf++ -48;
}
template<typename T, typename... Args> void read(T &first, Args& ... args) 
{
   read(first);
   read(args...);
}
int lastans = 0;
double h;
struct Line {
    double k, b;
    int l, r, flag, idx;
    Line(){}
    Line(double k, double b, int l, int r, int flag, int idx) {
        this->k = k, this->b = b, this->l = l, this->r = r, this->flag = flag, this->idx = idx;
    }
    double calc(int pos) {return k * pos + b;}//直线里面pos位置的y值
    int corss(const Line & rhs) const {return floor((b - rhs.b) / (rhs.k - k));}//线段交点的x坐标
}seg[maxn << 1];

inline void build(int rt, int l, int r) {
    seg[rt] = (Line){0.0,0.0,l,r,0,0};
    if(l == r) return;
    build(Lson);
    build(Rson);
}

inline void update(int cutl, int cutr, int rt,Line rhs) {
    if(rhs.l <= cutl && rhs.r >= cutr) {
        if(!seg[rt].flag) {seg[rt] = rhs,seg[rt].flag = 1;}
        else if(rhs.calc(cutl) - seg[rt].calc(cutl) > eps && rhs.calc(cutr) - seg[rt].calc(cutr) > eps) seg[rt] = rhs;
        else if(rhs.calc(cutl) - seg[rt].calc(cutl) > eps || rhs.calc(cutr) - seg[rt].calc(cutr) > eps) {
            int Mid = (cutl + cutr) >> 1;
            if(rhs.calc(Mid) - seg[rt].calc(Mid) > eps) swap(rhs,seg[rt]);
            if(rhs.corss(seg[rt]) - Mid < -eps) update(cutl, Mid, rt << 1, rhs);
            else update(Mid + 1, cutr, rt << 1|1,rhs);
        }
        
    } else {
        int Mid = (cutl + cutr) >> 1;
        if(rhs.l <= Mid) update(cutl,Mid, rt << 1,rhs);
        if(rhs.r > Mid) update(Mid + 1, cutr, rt << 1|1, rhs);
    }
}

inline void ask(int rt, int l, int r, int x) {
      double tmp = seg[rt].calc(x);
      if(tmp - h > eps) lastans = seg[rt].idx, h = tmp;
      else if(fabs(h - tmp) < eps) lastans = min(lastans,seg[rt].idx); 
      if(l == r) return;
      if(x <= mid) ask(Lson,x);
      if(x > mid) ask(Rson,x);
}
int main() {
    IOS;
    int T;
    cin >> T;
    build(1,1,100000);
    int Case = 1;
    while(T --){
        int op;
        cin >> op;
        if(op == 1) {
            int x0, y0, x1, y1;
            cin >> x0 >> y0 >> x1 >> y1;
            x0 = (x0 + lastans - 1) % modx + 1;
            x1 = (x1 + lastans - 1) % modx + 1;
            y0 = (y0 + lastans - 1) % mody + 1;
            y1 = (y1 + lastans - 1) % mody + 1;
            if(x0 == x1) {
               Line tmp = (Line){0.0,(double)max(y0,y1),min(x0,x1),max(x0,x1),0,Case};
               update(1,100000,0,tmp);
            } else {
               Line tmp = (Line){(double)(y1 - y0)/(double)(x1 - x0), (double)y0 - (double)(y1 - y0)/(double)(x1 - x0) * (double)x0,min(x0,x1),max(x0,x1),0,Case};
               update(1,100000,1,tmp);
            }
            Case++;
        } else {
            int k;
            cin >> k; h = -INF;
            k = (k + lastans - 1) % modx + 1;
            ask(1,1,100000,k);
            if(h == -INF) lastans = 0;
            cout << lastans << "\n";
        }
    }
    return 0;
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用和提到,Linux下安装ffmpeg是非常方便的。下面给出在Linux上安装ffmpeg的步骤: 1. 首先,需要安装Cygwin。Cygwin是一个在Windows上运行类似于Linux的环境的工具。可以从官方网站上下载并安装。 2. 安装完Cygwin后,打开终端,输入以下命令下载x264源代码并进行编译: ``` wget http://mirror.yandex.ru/mirrors/ftp.videolan.org/x264/snapshots/last_x264.tar.bz2 bunzip2 last_x264.tar.bz2 tar -vxf last_x264.tar cd last_x264 ./configure --enable-static --enable-shared --disable-asm --disable-avs make && sudo make install ``` 3. 然后,从ffmpeg官网下载ffmpeg源代码,并进行编译。编译方法可以参考官方文档或者在终端中输入以下命令: ``` wget http://ffmpeg.org/releases/ffmpeg-[版本号].tar.gz tar -xzvf ffmpeg-[版本号].tar.gz cd ffmpeg-[版本号] ./configure --enable-shared make sudo make install ``` 其中,是你想要安装的ffmpeg的版本号。 4. 编译完成后,就可以使用ffmpeg了。可以通过在终端中输入`ffmpeg`命令来验证是否安装成功。 请注意,以上步骤仅适用于在Linux系统上安装ffmpeg。如果你是在其他操作系统上进行安装,请参考相关的文档或者教程。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Windows下编译安装 FFmpeg](https://blog.csdn.net/heng615975867/article/details/119821945)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值