【BZOJ】4130: [PA2011]Kangaroos【KD树——最长连续1的子段长度】

传送门:【BZOJ】4130: [PA2011]Kangaroos【KD树】

题意:给出一个长度为 N(N5104) 的区间序列。然后接下来 M(M2105) 个询问,每个询问给出一个区间 [L,R] ,问区间序列中最长的连续子序列长度,使得连续子序列中每个区间和询问给出的区间存在交集。

分析:将询问 [L,R] 看成平面点 (L,R) ,构建 KD 树。将区间序列看成操作,每次操作是将一个矩阵范围+1,或者置0,那么对每个询问的点,其实就是其曾经连续被加的长度最长是多少。对于一个 [L,R] (序列中的点),询问 (x,y) 如果和其有交集,则须满足 yL xR

转化成这个之后接下来就是各种奇(e)怪(xin)的做法了。我的做法是,维护 KD 树每个节点5个标记,自己的出现过的最大值,自己的最近一次被置0的时刻,所代表区域的出现过的最大值,所代表区域最近一次被置0的时刻,所代表区域最早一次被置0的时刻。然后对于一个区域+1的更新,如果当前节点代表区域完全不在+1区域内,则这个区域我们修改一下他的置0标记(之后我们详细解释如何修改)。如果整个区域都要被+1,则直忽略。如果只是部分要+1,那么先判一下节点自己是不是要被修改(即置零),然后递归下去。

根据最早置0的时刻以及最晚置0的时刻,我们可以很方便的完成标记的下传,即被下传的节点的最大值变成下传节点最早减去当前最晚的,然后当前最晚变成下传的节点的最晚的。相当于把下传节点和被下传节点的信息压在一起。然后对于节点自己的更新,类似。

这题还有很多奇(gui)怪(chu)的写法,我觉得我这个已经算比较简单的了……

my   code:

#include <bits/stdc++.h>
using namespace std ;

typedef long long LL ;

#define clr( a , x ) memset ( a , x , sizeof a )

const int MAXN = 200005 ;
const int MAXM = 10000005 ;

struct Edge {
    int v , n ;
    Edge () {}
    Edge ( int v , int n ) : v ( v ) , n ( n ) {}
} ;

struct Node {
    int p[2] ;
    int f , l , r ;
    int Min[2] , Max[2] ;
    int maxv[2] , mins , maxs[2] ;
    int operator [] ( const int idx ) const {
        return p[idx] ;
    }
    void init () {
        Min[0] = Max[0] = p[0] ;
        Min[1] = Max[1] = p[1] ;
        mins = 0 ;
        maxs[0] = maxs[1] = 0 ;
        maxv[0] = maxv[1] = 0 ;
    }
    void up ( Node& a ) {
        Min[0] = min ( Min[0] , a.Min[0] ) ;
        Max[0] = max ( Max[0] , a.Max[0] ) ;
        Min[1] = min ( Min[1] , a.Min[1] ) ;
        Max[1] = max ( Max[1] , a.Max[1] ) ;
    }
    void down ( int x , int y ) {
        if ( x != -1 ) {
            if ( mins != -1 ) maxv[0] = max ( maxv[0] , x - maxs[0] - 1 ) ;
            else mins = x ;
            maxv[1] = max ( maxv[1] , x - maxs[1] - 1 ) ;
            maxs[0] = maxs[1] = y ;
        }
    }
} ;

char buf[MAXM + 1] , *cur = buf ;
Node T[MAXN] ;
int idx[MAXN] ;
int root , cmpw ;
int X[MAXN] , Y[MAXN] ;
int n , m ;

bool cmp ( const Node& a , const Node& b ) {
    return a.p[cmpw] < b.p[cmpw] ;
}

int build ( int l , int r , int w , int f ) {
    int o = l + r >> 1 ;
    cmpw = w ;
    nth_element ( T + l , T + o , T + r + 1 , cmp ) ;
    idx[T[o].f] = o ;
    T[o].f = f ;
    T[o].init () ;
    T[o].l = l != o ? build ( l , o - 1 , !w , o ) : 0 ;
    T[o].r = r != o ? build ( o + 1 , r , !w , o ) : 0 ;
    if ( T[o].l ) T[o].up ( T[T[o].l] ) ;
    if ( T[o].r ) T[o].up ( T[T[o].r] ) ;
    return o ;
}

int check ( int x1 , int x2 , int y1 , int y2 , int x , int y ) {
    int cnt = 0 ;
    if ( x1 <= x && y1 >= y ) ++ cnt ;
    if ( x1 <= x && y2 >= y ) ++ cnt ;
    if ( x2 <= x && y1 >= y ) ++ cnt ;
    if ( x2 <= x && y2 >= y ) ++ cnt ;
    if ( cnt == 4 ) return 1 ;
    if ( cnt ) return 2 ;
    return 0 ;
}

void push_down ( int o ) {
    int x = T[o].mins , y = T[o].maxs[0] ;
    if ( T[o].l ) T[T[o].l].down ( x , y ) ;
    if ( T[o].r ) T[T[o].r].down ( x , y ) ;
    T[o].mins = -1 ;
}

void upd ( int o , int x , int y , int t ) {
    int d = check ( T[o].Min[0] , T[o].Max[0] , T[o].Min[1] , T[o].Max[1] , x , y ) ;
    if ( !d ) {
        T[o].down ( t , t ) ;
        return ;
    }
    if ( d == 1 ) return ;
    if ( T[o][0] > x || T[o][1] < y ) {
        T[o].maxv[1] = max ( T[o].maxv[1] , t - T[o].maxs[1] - 1 ) ;
        T[o].maxs[1] = t ;
    }
    push_down ( o ) ;
    if ( T[o].l ) upd ( T[o].l , x , y , t ) ;
    if ( T[o].r ) upd ( T[o].r , x , y , t ) ;
}

void down ( int o , int ans ) {
    push_down ( o ) ;
    T[o].maxv[1] = max ( T[o].maxv[1] , n - T[o].maxs[1] ) ;
    T[o].maxv[0] = max ( T[o].maxv[0] , ans ) ;
    T[o].maxv[1] = max ( ans , T[o].maxv[1] ) ;
    T[o].maxv[1] = max ( T[o].maxv[0] , T[o].maxv[1] ) ;
    if ( T[o].l ) down ( T[o].l , T[o].maxv[0] ) ;
    if ( T[o].r ) down ( T[o].r , T[o].maxv[0] ) ;
}

void scanf ( int& x ) {
    while ( *cur < '0' ) ++ cur ;
    x = *cur - '0' , ++ cur ;
    while ( *cur >= '0' ) ( x *= 10 ) += *cur - '0' , ++ cur ;
}

void solve () {
    for ( int i = 1 ; i <= n ; ++ i ) {
        scanf ( Y[i] ) ;
        scanf ( X[i] ) ;
    }
    for ( int i = 1 ; i <= m ; ++ i ) {
        scanf ( T[i].p[0] ) ;
        scanf ( T[i].p[1] ) ;
        T[i].f = i ;
    }
    root = build ( 1 , m , 0 , 0 ) ;
    for ( int i = 1 ; i <= n ; ++ i ) {
        upd ( root , X[i] , Y[i] , i ) ;
    }
    down ( root , 0 ) ;
    for ( int i = 1 ; i <= m ; ++ i ) {
        printf ( "%d\n" , T[idx[i]].maxv[1] ) ;
    }
}

int main () {
    fread ( buf , 1 , MAXM , stdin ) ;
    scanf ( n ) ;
    scanf ( m ) ;
    solve () ;
    return 0 ;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值