物理( 线段树套单调队列 )

sg需要从每个小弟那里接收消息,小弟们的声波频率接收范围不同,且声音传播有限距离。求每个小弟传话给sg所需时间。采用线段树与单调队列优化,通过维护频率范围内的最小值,解决区间长度问题,提高效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

sg站在1号点,后面有他的n-1个小弟。现在妹子想告诉sg一些话,但是她不知道选谁。由于sg的小弟们构造奇特,他们能听到的声波频率也是不一样的。第i个人可以发出并接收声波频率为[xi,yi]的声音,并且每个人的声音最多只能传播不超过l的距离。每一次说话并被另一个人接收到的过程都要消耗1单位时间。

现在给出所有人能发出并接收的声波频率的范围和位置,sg的妹子想知道,每个小弟传话给sg要多久。由于sg很牛逼,他可以接收所有小弟发出的声波,当然前提要在距离之内。sg的位置为0。

Input

第一行:n,l,含义见题意。

第二行至第n行:xi,yi,li,[xi,yi]是第i个小弟能发出并接收的声波频率范围,li是第i个小弟的位置。

Output

第一行至第n-1行:第i行表示第i+1个小弟要传话给sg所消耗的时间。

对于暴力 n2 的dp显然f[i] = minSectioniSectionj!=nullANDposiposj<=L ( f[j] ) + 1

然后我们考虑维护min的部分;
考虑开一个线段树,以频率为下标。
然后呢? 怎么解决长度问题?总不可能在线段树每个节点塞一个线性表每次遍历扫一遍
显然 posi 是增函数 , 我们如果用单调队列代替线性表当然好多了。
怎么维护这个线段树呢?
每个节点开两个单调队列,cover[] && pass[]
对于Insert操作,每经过一个节点更新一下pass[],遇到整段配对的区间更新cover[]
对于Ask操作,每经过一个节点用cover[]来更新遇到整段配对的区间更新pass[],

时间虽然给了5s仍然有点紧,有些小优化,如
cover被更新的节点不用更新pass
不要用map离散化数据
只在Ask操作内删除队首非法节点

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>

using namespace std ;

#define N 250010
#define psb( x ) push_back( x ) 

int i , j , k , n , L ;

map< int , int > s ;

struct node {
    int x , y , l , ps ;
}z[N] ;

// We Store id 
struct SegmentTree_With_Queue {
    vector<int> cover , pass ;
    int cl , cr , pl , pr  ;
    // Remember to Initialize 
}tr[N*2*4] ;

int f[N] , demap[N*2] ;

struct For_Dis {
    int ps , va ;
    bool x ;
}a[N*2] ;

bool cmp( For_Dis a , For_Dis b ) {
    return a.va < b.va ;
}

bool _cmp( node a , node b ) {
    return a.l < b.l ;
}

void Ins( int _l , int _r , int l , int r , int va , int po ) {
    int m = l+r >> 1 ;
    if( _l==l && _r==r ) {
        while( tr[po].cl<=tr[po].cr && f[ tr[po].cover[ tr[po].cr ] ] > f[va] ) tr[po].cr-- ;
        if( ( tr[po].cr + 1 ) >= tr[po].cover.size() ) tr[po].cover.psb( 0 );
        tr[po].cover[ ++tr[po].cr ] = va ;
        return ;
    }
    while( tr[po].pl<=tr[po].pr && f[ tr[po].pass[ tr[po].pr ] ] > f[va] ) tr[po].pr -- ;
    if( ( tr[po].pr+1 ) >= tr[po].pass.size() ) tr[po].pass.psb( 0 ) ;
    tr[po].pass[ ++tr[po].pr ] = va ;
    if( _r<=m ) Ins( _l,_r,l,m,va,po*2 ) ;
        else if( _l>m ) Ins( _l,_r,m+1,r,va,po*2+1 ) ;
            else Ins( _l,m,l,m,va,po*2 ) , Ins( m+1,_r,m+1,r,va,po*2+1 );

}

int cur , ax = 0 ;

void Ask( int _l , int _r , int l , int r , int po  ) {
    int m = l + r >> 1 ;
    while( tr[po].cl<=tr[po].cr && -z[ tr[po].cover[ tr[po].cl ] ].l  + z[ cur ].l  > L ) tr[po].cl ++ ;
    if( tr[po].cl <= tr[po].cr ) if( ax==-1 || f[ax]>f[ tr[po].cover[ tr[po].cl ] ] ) ax = tr[po].cover[ tr[po].cl ] ;
    if( l==_l && r==_r ) {
        while( tr[po].pl<=tr[po].pr && -z[ tr[po].pass[ tr[po].pl ] ].l + z[ cur ].l  > L ) tr[po].pl ++ ;
        if( tr[po].pl <= tr[po].pr ) if( ax==-1 || f[ax]>f[ tr[po].pass[ tr[po].pl ] ] ) ax = tr[po].pass[ tr[po].pl ] ;
        return ;
    }     
    if( _r<=m ) Ask( _l , _r , l , m , po * 2 ) ;
        else if( _l>m ) Ask( _l , _r , m+1 , r , po * 2 + 1 ) ;
            else Ask( _l , m , l , m , po * 2 ) , Ask( m+1 , _r , m+1 , r , po * 2 + 1 ) ;
}

void BuildTree( int l , int r , int po ) {
    tr[po].cl = tr[po].pl = 1 , tr[po].cr = tr[po].pr = 0 ;
    tr[po].cover.psb( 0 ) , tr[po].pass.psb( 0 ) ;
    tr[po].cover.psb( 0 ) , tr[po].pass.psb( 0 ) ;
    if( l==r ) return ;
    BuildTree( l , l + r >> 1 , po << 1 ) ;
    BuildTree( ( r + l >> 1 ) + 1 , r , ( po << 1 ) + 1 ) ;
}

int ans[N] ;

int main() {
    scanf("%d%d",&n,&L ) ;
    --n  ;
    for( i=1 ; i<=n ; i++ ) {
        scanf("%d%d%d",&z[i].x,&z[i].y,&z[i].l ) ;
        a[i*2-1].va = z[i].x ;
        a[i*2].va = z[i].y ;
        a[i*2-1].ps = a[i*2].ps = i ;
        a[i*2-1].x = 1 ;
        z[i].ps = i ;
    }
    sort( a+1 , a+1+n*2 , cmp ) ;
    int disva = 0 ;
    a[0].va = -1 ;
    for( i=1 ; i<=n*2 ; i++ )   {
        if( a[i].va!=a[i-1].va ) ++ disva ;
        if( a[i].x ) z[ a[i].ps ].x = disva ;
            else z[ a[i].ps ].y = disva ;
    }
    sort( z+1 , z+1+n , _cmp ) ;
    BuildTree( 1 , disva , 1 ) ;
    tr[1].cover.psb( 0 ) ;
    tr[1].cl = tr[1].cr = 1 ;
    f[0] = 0 ;
    for( i=1 ; i<=n ; i++ ) {
        cur = i ;
        ax = -1 ;
        Ask( z[i].x , z[i].y , 1 , disva , 1 ) ;
        if( ax == -1 ) {
            ans[ z[i].ps ] = -1 ;
            continue ;
        }
        f[i] = f[ax] + 1 ;
        Ins( z[i].x , z[i].y , 1 , disva , i , 1 ) ;
        ans[ z[i].ps ] = f[i] ;
    }
    for( i=1 ; i<=n ; i++ ) printf("%d\n",ans[i] ) ;
}

DebugLogo
1 删队首不等式符号恰好写反
2 更新区间最小值判断语句符号写反

写比较长的布尔式要慢点写,图快反而得不偿失。

<think>嗯,用户想了解线段树线段树的实现和应用场景。首先,我需要回忆一下什么是线段树线段树。根据之前的引用内容,比如引用[1]提到,这是一种二维线段树的实现方法,外层处理行,内层处理列。所以,这应该用于处理二维区间的问题,比如矩阵的查询和更新。 接下来,用户可能想知道具体的实现方式。记得引用[2]说过,二维线段树是先按行构建外层线段树,每个节点再按列构建内层线段树。这样分层结构应该能处理二维区域的查询。需要详细说明外层和内层如何协作,可能还要提到每个节点的结构,比如外层节点代表行区间,内层节点处理对应的列区间。 然后应用场景方面,用户可能想知道在哪些实际问题中会用到这种结构。比如矩阵区域求和、最值查询,或者动态二维范围统计,比如游戏中的地图更新。引用[4]提到树树属于高级数据结构,可能应用在需要高效处理二维数据的场景,比如图像处理或地理信息系统。 另外,需要考虑这种数据结构的优缺点。引用[3]提到其他结构如K-D树或二进制分组可能有不同的适用场景,所以需要对比一下。比如线段树线段树的时间复杂度是O(logN * logM),适用于静态或动态二维数据,但可能空间消耗较大,因为每个外层节点都有内层线段树。 用户还可能想知道具体的代码结构,比如如何嵌实现。可能需要分步骤说明,先构建外层,再在每个节点构建内层,然后处理查询和更新操作。例如,更新某个点时,先在外层找到对应的行区间,再在内层更新列的位置。查询时则需要在多个外层和内层节点上聚合结果。 还要注意正确使用LaTeX格式,比如数学表达式要用$...$,独立公式用$$...$$。比如矩阵范围求和可以写成公式,时间复杂度也要用LaTeX表示。 可能用户没有说出来的深层需求是,如何在实际项目中应用这种结构,或者与其他数据结构的选择比较。因此,在回答时应该涵盖实现步骤、应用例子以及优缺点比较,帮助用户全面理解。 最后,确保引用来源正确,引用[1]和[2]是关于二维线段树的,而引用[3]和[4]提到了其他树结构和应用场景,可能需要适当引用来说明对比和适用范围。</think>### 线段树线段树的实现与应用场景 #### 一、实现方式 线段树线段树(二维线段树)是一种嵌结构,外层线段树管理行维度,每个外层节点再嵌一棵内层线段树管理列维度[^1][^2]。 **实现步骤**: 1. **外层线段树**:按行构建,每个节点表示行区间 $[L, R]$ 2. **内层线段树**:在每个外层节点中按列构建,处理对应行区间内的列区间查询 3. **数据存储**:外层节点的每个叶子节点对应一行数据,其内层线段树存储该行的列数据 **示例代码框架**(伪代码): ```python class OuterNode: inner_tree = InnerSegmentTree() # 内层线段树 class TwoDimensionalSegmentTree: def build(self, rows): # 外层构建 for row in rows: outer_node = build_outer_tree(row) outer_node.inner_tree.build(row.columns) # 内层构建 def query(self, x1, x2, y1, y2): # 外层查询行区间 [x1, x2],内层查询列区间 [y1, y2] return outer_query(x1, x2).inner_query(y1, y2) def update(self, x, y, value): # 外层定位行x,内层更新列y outer_locate(x).inner_update(y, value) ``` #### 二、时间复杂度分析 - **单点更新**:$O(\log N \cdot \log M)$(外层树深度 $\log N$,内层树深度 $\log M$) - **区域查询**:$O(\log N \cdot \log M)$ - **空间复杂度**:$O(N \cdot M)$(最坏情况下) #### 三、应用场景 1. **动态矩阵区域操作** - 实时更新矩阵元素值 - 查询子矩阵的和/最大值,例如: $$ \sum_{i=a}^{b} \sum_{j=c}^{d} matrix[i][j] $$ 2. **地理信息系统** - 动态统计矩形区域内的点数量(如地图热点分析) 3. **图像处理** - 对图像局部区域进行像素值统计或滤波操作[^2] #### 四、与其他结构的对比 | 结构类型 | 适用场景 | 时间复杂度 | 空间复杂度 | |-----------------|--------------------------|------------------|-------------| | 线段树线段树 | 动态二维区间查询/更新 | $O(\log^2 n)$ | $O(n^2)$ | | K-D Tree | 静态高维近邻搜索 | $O(n^{1-1/d})$ | $O(n)$ | | 二维前缀和 | 静态矩阵区域求和 | $O(1)$查询 | $O(n^2)$ | #### 五、优化方向 1. **惰性标记扩展**:在内层线段树中引入惰性传播,支持区间批量更新 2. **空间压缩**:对稀疏矩阵使用动态开点技术[^3] 3. **混合结构**:对行使用线段树,对列使用树状数组以降低常数因子[^4]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值