数据结构专题-线段树专题 A 单点更新 区间查询 B 区间更新 区间查询 D 线段树+二分

A
思路:线段树基本操作。

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL long long 
using namespace std;
const int AX = 1e6+6;
LL s[AX<<2];
LL a[AX<<2];
LL b[AX<<2];
void pushUp( int rt ){
    s[rt] = s[rt<<1] + s[rt<<1|1];
    a[rt] = max( a[rt<<1] , a[rt<<1|1] );
    b[rt] = min( b[rt<<1] , b[rt<<1|1] );
}
void update( int site , int v , int l , int r , int rt ){
    if( l == r ){
        s[rt] = v;
        a[rt] = v;
        b[rt] = v;
        return;
    }
    int mid = ( l + r ) >> 1;
    if( site <= mid ) update( site , v , l , mid , rt << 1 );
    if( mid < site ) update( site , v , mid + 1 , r , rt << 1 | 1 );
    pushUp(rt);
}

LL query_SUM( int L , int R , int l , int r , int rt ){
    if( L <= l && R >= r ){
        return s[rt];
    }
    int mid = ( l + r ) >> 1;
    LL sum = 0LL;
    if( L <= mid ){
        sum += query_SUM( L , R , l , mid , rt << 1 );
    }
    if( mid < R ){
        sum += query_SUM( L , R , mid + 1 , r , rt << 1 | 1 );
    }
    return sum ;
}

int query_MAX( int L , int R , int l , int r , int rt ){
    if( L <= l && R >= r ) return a[rt];
    int ans = -INF;
    int mid = ( l + r ) >> 1;
    if( L <= mid) ans = max( ans , query_MAX( L , R , l , mid , rt << 1 ) ) ;
    if( R > mid ) ans = max( ans , query_MAX( L , R , mid + 1 , r , rt << 1 | 1 ) ) ;
    return ans ;
}

int query_MIN( int L ,int R , int l , int r , int rt ){
    if( L <= l && R >= r ) return b[rt];
    int ans = INF;
    int mid = ( l + r ) >> 1 ;
    if( L <= mid ) ans = min( ans , query_MIN( L , R , l , mid , rt << 1 ));
    if( R > mid ) ans = min( ans , query_MIN( L , R , mid + 1 , r , rt << 1 | 1 ));
    return ans ;
}

int main(){
    int n , m;
    memset( s , 0 , sizeof(s) );
    memset( a , 0 , sizeof(a) );
    memset( b , 0 , sizeof(b) );
    scanf("%d%d",&n,&m);
    int op;
    int x, y;
    while( m-- ){
        scanf("%d",&op);
        if( !op ){
            scanf("%d%d",&x,&y);
            update( x , y , 1 , n , 1 );
        }else{
            int L , R;
            scanf("%d%d",&L,&R) ;

            int maxn = query_MAX(L,R,1,n,1);
            int minus = query_MIN(L,R,1,n,1);
            LL sum = query_SUM(L,R,1,n,1);
            printf("%lld\n",sum-maxn-minus);

        }
    }
    return 0 ;
}

B

Code:

#include <bits/stdc++.h>
#define LL long long
using namespace std;
int n, m ;
const int AX = 1e6+6;
LL s[AX<<2];
int lazy[AX<<2];
LL res ;
void pushUp( int rt ){
    s[rt] = s[rt<<1] + s[rt<<1|1];
    return ;
} 

void pushDown( int rt , int L , int R ){
    if( lazy[rt] ){
        int mid = ( L + R ) >> 1 ;
        s[rt<<1] += ( mid - L + 1 ) * lazy[rt] ;
        s[rt<<1|1] += ( R - mid ) * lazy[rt];
        lazy[rt<<1] += lazy[rt];
        lazy[rt<<1|1] += lazy[rt];
        lazy[rt] = 0;
    }
    return ;
}

void update( int L , int R , int v , int l , int r , int rt ){
    if( L <= l && R >= r ){
        s[rt] += ( r - l + 1 ) * v;
        lazy[rt] += v;
        return;
    }
    pushDown(rt,l,r);
    int mid = ( l + r ) >> 1;
    if( L <= mid ) update( L , R , v , l ,mid , rt << 1 );
    if( R > mid ) update( L , R , v , mid + 1 , r , rt << 1 | 1 );
    pushUp(rt);
}

LL Query( int L , int R , int l , int r , int rt ){
    if( L <= l && R >= r ){
        //res += s[rt];
        return s[rt];
    }
    pushDown(rt,l,r);
    int mid = ( l + r ) >> 1 ;
    LL res = 0;
    if( L <= mid ) res += Query( L , R , l , mid , rt << 1 );
    if( R > mid )   res += Query( L , R , mid + 1, r , rt << 1 | 1 );
    return res;
}

int main(){
    scanf("%d%d",&n,&m);
    memset( lazy , 0 , sizeof(lazy) );
    memset( s , 0 , sizeof(s) );
    while( m -- ){
        int op ;
        int L , R , v;
        scanf("%d%d%d%d",&op,&L,&R,&v);
        if( op == 0 ){
            update( L , R , v , 1 , n , 1 );
        }else{
            res = 0;
            LL res = Query( L , R , 1 , n , 1 );
            printf("%lld\n",res);
        }
    }
    return 0;
}

C
思路:
假设每个Ci = i , bn最大为n+1.
对于每个Ci , 相当于询问b(i-ci) – b(i-1)区间内最小未出现的数。

我们可以设s[x] 为x最后出现的位置。对于每个Ci,我们可以得到左区间 i - Ci;
s[1] = 0 , 其他位置初始化-1(其他值都未出现过,所以都满足s[x] < i - ci )

凡是s【x】< i - Ci 的x均未在区间内出现过,那么我们只要找出最小的x,满足:
s[x] < i - ci即可。
可以用线段树维护s[x] ,1 <= x < n+1 ;区间的最小值。
每次给出左区间 i -ci ,就从最大的区间查询,如果左子树的最小值>=i-ci,就递归访问右子树,否则访问左子树。
最终区间长度为1时,l(或者r)即为答案。

Code:

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#define INF 0x3f3f3f3f
using namespace std;
const int AX = 1e6+6;
int s[AX<<2];
int res[AX];
int n;
int tot ;
void pushUp( int rt ){
    s[rt] = min( s[rt<<1] , s[rt<<1|1] ) ;
    return ;
}

void update( int site , int v , int l , int r , int rt ){
    if( l == r ){
        s[rt] = v;
        return;
    }
    int mid = ( l + r ) >> 1 ;
    if( site <= mid ) update( site , v , l , mid , rt << 1 );
    if( site > mid ) update( site , v , mid + 1 , r , rt << 1 | 1 );
    pushUp(rt);
}

int query( int L , int R , int l , int r , int rt ){
    if( L <= l && R >= r ){
        return s[rt];
    }
    int mid  = ( l + r ) >> 1 ;
    int res = INF;
    if( L <= mid ) res = min( res , query( L , R , l , mid , rt << 1 ) );
    if( R > mid ) res = min( res , query( L , R , mid + 1 , r , rt << 1 | 1 ) ); 
    return res;
}

int solve( int l , int r , int rt , int v ){
    if( l == r ){
        return l;
    }
    int mid = ( l + r ) >> 1;
    if( s[rt<<1] >= v ){
        return solve( mid + 1 , r , rt << 1 | 1,  v );
    }else{
        return solve( l , mid , rt << 1 , v );
    }
}

int main(){
    scanf("%d",&n);
    int x;
    tot = 0;
    memset( s,  -1 , sizeof(s) );
    update( 1 , 0 , 1 , n + 1 , 1 );
    for( int i = 1 ; i <= n ; i++ ){
        scanf("%d",&x);
        int v = i - x  ;
        res[i] = solve( 1 , n + 1 , 1 , v );
        update( res[i] , i , 1 , n + 1 , 1 );
    } 
    for( int i = 1 ; i < n ; i++ ){
        printf("%d ",res[i]);
    }printf("%d\n",res[n]);
    return 0;
}

D
思路:
线段树+二分答案
结果只要求a[k]的值,并且a数列是1-n的一个全排列,那么可以二分答案。
假设mid为答案,将大于mid的设为1,小于等于的设为0
当对【L,R】区间进行排序时,
1.先求出区间内1的个数。
2.区间全置为0.
3.升序排序时,将最后c个数 ( 【R-c+1,R】 ) 置为1.
4.降序排序时,将前c个数( 【 L,L + c - 1 】)置为1.
所有操作进行结束后,如果【k,k】== 1 说明a【k】> mid ( -> li = mid + 1 ) ,为0说明 a[k] < mid ( -> ri = mid ) .
Code:

#include <bits/stdc++.h>
using namespace std;
const int AX = 1e5+66;
int lazy[AX<<2];
int s[AX<<2];
int a[AX];
int op[AX];
int l[AX];
int r[AX];
int n,k;
int tot;

void pushDown( int rt , int L , int R ){
    if( lazy[rt] != -1 ){
        int mid = ( L + R ) >> 1 ;
        s[rt<<1] = ( mid - L + 1 ) * lazy[rt] ;
        s[rt<<1|1] = ( R - mid ) * lazy[rt];
        lazy[rt<<1] = lazy[rt];
        lazy[rt<<1|1] = lazy[rt];
        lazy[rt] = -1;
    }
    return ;
}


void pushUp( int rt ){
    s[rt] = s[rt<<1] + s[rt<<1|1];
    return ;
}

void build( int l , int r , int rt , int v ){
    lazy[rt] = -1;
    if( l == r ){
        s[rt] =  ( a[l] > v ) ;
        return ; 
    }
    int mid = ( l + r ) >> 1 ;
    build( l , mid , rt << 1 , v );
    build( mid + 1 , r , rt << 1 | 1 , v);
    pushUp(rt);
}

void update( int L , int R , int v , int l , int r , int rt ){
    if( L <= l && R >= r ){
        s[rt] = v * ( r - l + 1 );
        lazy[rt] = v;
        return;
    }
    pushDown(rt,l,r);
    int mid = ( l + r ) >> 1;
    if( L <= mid ) update( L , R , v , l , mid , rt << 1 );
    if( R > mid ) update( L , R , v , mid + 1 , r , rt << 1 | 1);
    pushUp(rt);
}

int query( int L , int R , int l , int r , int rt ){
    if( L <= l && R >= r ){
        return s[rt];
    }
    pushDown(rt,l,r);
    int ans = 0 ;
    int mid = ( l + r ) >> 1;
    if( L <= mid ) ans += query( L , R , l , mid, rt << 1 );
    if( R > mid )  ans += query( L , R , mid + 1, r , rt << 1 | 1 );
    return ans ;
}

int main(){
    scanf("%d%d",&n,&k);
    for( int i = 1 ; i <= n ; i++ ){
        scanf("%d",&a[i]);
    }
    int m ;
    scanf("%d",&m);
    for( int i = 0 ; i < m ; i++ ){
        scanf("%d%d%d",&op[i],&l[i],&r[i]);     
    }
    int li = 1 , ri = n ; 
    while( li < ri ){
        int mid = ( li + ri ) >> 1 ;
        build( 1 , n , 1 , mid );
        for( int i = 0 ; i < m ; i++ ){
            int L = l[i];
            int R = r[i];
            int c = query( L , R , 1 , n , 1 );
            update( L , R , 0 , 1 , n , 1 );
            if( op[i] ){  
                if( L <= L + c - 1 ){
                    update( L , L + c - 1 , 1 , 1, n , 1 );
                } 
            }else{
                if( R - c + 1 <= R ){
                    update( R - c + 1 , R , 1 , 1 , n , 1 );
                }
            }
        }
        if( query( k , k , 1 , n , 1 ) ){
            li = mid + 1 ;
        }else ri = mid  ;
    }
    printf("%d\n",li);
    return 0 ;
}

猜你喜欢

转载自blog.csdn.net/frankax/article/details/80328280