bzoj3165 segment 超哥线段树

链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3165

题意:动态增加线段,求出横坐标纵坐标最高的被覆盖点所在线段。

这个题要用到李超线段树(orz李超)……大概这是李超那篇论文出现后第二年的题?不管了直接介绍这一数据结构。李超线段树的目的就是查询出每个点被线段覆盖的情况,操作无非就是插入线段和查询两件事。

插入线段:首先计算出原有线段和现在线段在这个区间之内的函数值,如果新线段一直大直接修改返回,如果一直小也直接返回,否则递归修改至单点或全部返回为止。

查询线段:不断地递归查询,从根节点整条数轴一直查询到单点,期间找到最值就进行修改。

可以证明,单次修改时间复杂度$O(log^2n)$,单次查询时间复杂度$O(logn)$,因此复杂度完全足够。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<map>
 6 using namespace std;
 7 const int maxn=100005,maxp=40005;const double eps=1e-9;
 8 struct node
 9 {
10     double k,b;int id;bool vis;
11 }S[maxn<<2];
12 int dcmp(double x)
13 {
14     return (x>eps)-(x<-eps);
15 }
16 #define mid ((l+r)>>1)
17 #define lc root<<1
18 #define rc root<<1|1
19 #define lson lc,l,mid
20 #define rson rc,mid+1,r
21 inline void solve(int root,int l,int r,int idx,double k,double b)
22 {
23     if(!S[root].vis)
24     {
25         S[root].k=k,S[root].b=b,S[root].id=idx,S[root].vis=1;
26         return;
27     }
28     double y0=S[root].k*l+S[root].b,y1=k*l+b,y2=S[root].k*r+S[root].b,y3=k*r+b;
29     if(dcmp(y1-y0)>0&&dcmp(y3-y2)>0)
30     {
31         if(dcmp(k-S[root].k)==0&&dcmp(b-S[root].b)==0)return;
32         S[root].k=k,S[root].b=b,S[root].id=idx;
33         return;
34     }
35     if(dcmp(y1-y0)<=0&&dcmp(y3-y2)<=0)return;
36     solve(lson,idx,k,b);solve(rson,idx,k,b);
37 }
38 inline void insert(int root,int l,int r,int L,int R,int idx,double k,double b)
39 {
40     if(L<=l&&r<=R){solve(root,l,r,idx,k,b);return;}
41     if(L<=mid)insert(lson,L,R,idx,k,b);if(R>mid)insert(rson,L,R,idx,k,b);
42 }
43 inline void insert(int x0,int x1,int y0,int y1,int num)
44 {
45     int L=x0,R=x1;double k=1.0*(y1-y0)/(1.0*(x1-x0)),b=y0-x0*k;int idx=num;insert(1,1,maxp,L,R,idx,k,b);
46 }
47 double ans;int ans_id;
48 inline void query(int root,int l,int r,int pos)
49 {
50     double x=S[root].k*pos+S[root].b;
51     if(dcmp(x-ans)>0||dcmp(x-ans)==0&&S[root].id<ans_id)ans=x,ans_id=S[root].id;
52     if(l==r)return;
53     if(pos<=mid)query(lson,pos);else query(rson,pos);
54 }
55 int a[maxn],id[maxn];
56 int haha()
57 {
58     int n;scanf("%d",&n);int lastans=0;
59     int opt,x0,y0,x1,y1,x,num=0;
60     while(n--)
61     {
62         scanf("%d",&opt);
63         if(!opt)
64         {
65             scanf("%d",&x),x=((x+lastans-1)%39989+1);
66             ans=ans_id=0;query(1,1,maxp,x);
67             if(ans<a[x]||ans==a[x]&&id[x]<ans_id)ans_id=id[x];
68             printf("%d\n",lastans=ans_id);
69         }
70         else
71         {
72             num++;scanf("%d%d%d%d",&x0,&y0,&x1,&y1);
73             x0=(x0+lastans-1)%39989+1,y0=(y0+lastans-1)%(int)1e9+1,x1=(x1+lastans-1)%39989+1,y1=(y1+lastans-1)%(int)1e9+1;
74             if(x0>x1)swap(x0,x1),swap(y0,y1);
75             if(x0==x1)
76             {
77                 if(max(y0,y1)>a[x0])a[x0]=max(y0,y1),id[x0]=num;
78             }
79             else insert(x0,x1,y0,y1,num);
80         }
81     }
82 }
83 int sb=haha();
84 int main(){;}

bzoj3165

时间: 2024-08-06 14:06:59

bzoj3165 segment 超哥线段树的相关文章

BZOJ 1568: [JSOI2008]Blue Mary开公司(超哥线段树)

1568: [JSOI2008]Blue Mary开公司 Time Limit: 15 Sec  Memory Limit: 162 MBSubmit: 1080  Solved: 379[Submit][Status][Discuss] Description Input 第一行 :一个整数N ,表示方案和询问的总数. 接下来N行,每行开头一个单词“Query”或“Project”. 若单词为Query,则后接一个整数T,表示Blue Mary询问第T天的最大收益. 若单词为Project,则

Codeforces 242E. XOR on Segment (二维线段树 lazy操作 xor)

题目链接: http://codeforces.com/problemset/problem/242/E 题意: 给出一个序列,有两种操作,一种是计算l到r的和,另一种是让l到r的数全部和x做异或运算. 思路: from: http://blog.csdn.net/u013912596/article/details/39006317 很显然直接暴力是不可能的,又是两种操作,又想到了线段树..但是这并不简单,异或操作该怎么处理? 异或是一种位运算,如果x的第j位是1,那么说明l到r的每个数的第j

HDU 5372 Segment Game(线段树+离散化)

题意: 有两种操作: 1. 插入一个线段 2. 删除一个已存在的线段 每次插入后输出当前插入的线段能完整覆盖存在的几条线段. 解析: 线段树上面维护的是两个值,左端点的和,右端点的和 每次插入一条区间[L, R]就, 先询问 [0, R] 的右端点个数 lsum 再询问[L, INF]的左端点的个数 rsum tot表示:当前线段还有几条 那么题目要求的是整条线段的个数就是:lsum+rsum?tot (这里用到了容斥的思想) 再用线段树(或者树状数组)单点增加左节点的个数,和右节点的个数 删除

线段树---HDU1166敌兵布阵

这个是线段树中最入门的题目,但是由于不了解线段树的概念,当然更不知道怎么样,所以觉得挺费劲,整了一会发现还是基本的思想,就是还是将一个线段继续分割,一直分割到不能分割,这道题目是知道多少个军营,也就是区间为1-n, 将它分割, 建立树, 可以不用保存它区间的左端点和右端点,用数组下标代表就可以了, 数组的值代表当前军营里人的个数,然后这个题就是单个点的增加或者减少,其实增加减少都是增加,减少只是把增加的数目变成负数就行了,还有就是更新完最下面的点还要一直往上更新.那样查找区间的时候才不会出错.下

超全面的线段树:从入门到入坟

超全面的线段树:从入门到入坟 \(Pre\):其实线段树已经学了很久了,突然线段树这个数据结构比较重要吧,现在想写篇全面的总结,帮助自己复习,同时造福广大\(Oier\)(虽然线段树的思维难度并不高).本篇立志做一篇最浅显易懂,最全面的线段树讲解,采用\(lyd\)写的<算法竞赛进阶指南>上的顺序,从最基础的线段树到主席树,本篇均会涉及,并且附有一定量的习题,以后可能会持续更新,那么现在开始吧! 目录 更新日志 线段树想\(AC\)之基本原理(雾*1 线段树想偷懒之懒标记(雾*2 线段树想应用

Aizu 2450 Do use segment tree 树链剖分+线段树

Do use segment tree Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.bnuoj.com/v3/problem_show.php?pid=39566 Description Given a tree with n (1 ≤ n ≤ 200,000) nodes and a list of q (1 ≤ q ≤ 100,000) queries, process the queries in order and out

线段树+离散化 IP地址段检查 SEGMENT TREE

Problem: Give a series of IP segments, for example, [0.0.0.1-0.0.0.3], [123.234.232.21-123.245.21.1]... Now there is a new IP, find which IP segment it's in ? Solution: First, we could map the ends of IP segments into some intervals, since the IP add

HDU 4107 Gangster Segment Tree线段树

这道题也有点新意,就是需要记录最小值段和最大值段,然后成段更新这个段,而不用没点去更新,达到提高速度的目的. 本题过的人很少,因为大部分都超时了,我严格按照线段树的方法去写,一开始居然也超时. 然后修补了两个地方就过了,具体修改的地方请参看程序. 知道最大值段和最小值段,然后修补一下就能过了.不是特别难的题目. #include <stdio.h> #include <string> #include <algorithm> using namespace std; c

线段树(segment tree)

1.概述 线段树,也叫区间树,是一个完全二叉树,它在各个节点保存一条线段(即"子数组"),因而常用于解决数列维护问题,基本能保证每个操作的复杂度为O(lgN). 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点. 对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b].因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度. 使用线段树可以