[BZOJ4540]-[Hnoi2016]序列-线段树维护信息

说在前面

马上就要NOI了啊…


题目

BZOJ4540传送门
看题可戳传送门


解法

这是一道,「不带修改、区间询问」类问题。很容易想到莫队
对于这一类题,也可以使用线段树来解决。离线所有询问,从左到右依次加入信息,对于已经加入的位置 i i ,维护 inow i ∼ n o w 的信息。在相应的位置处理区间询问

然后这道题,题解可以看这位的,传送门,思路很清晰
这道题使用类似矩阵乘法的东西去维护,就是因为其标记只有「加法」和「乘法」,而这个东西满足结合律(动态dp也是类似思想吧

感觉推广性挺强的,mark一下


下面是代码

    Problem: 4540
    User: Izumihanako
    Language: C++
    Result: Accepted
    Time:3256 ms
    Memory:16060 kb
****************************************************************/
 
#include 
#include 
#include 
using namespace std ;
 
long long ans[100005] ;
int N , Q , a[100005] ;
struct Queries{
    int L , R , id ;
    bool operator < ( const Queries &A ) const {
        return R < A.R ;
    }
} q[100005] ;
 
struct Node{
    Node *ch[2] ;
    long long len , val , sum ;
    long long a , b , c , d ;
    void muls( long long A , long long B , long long C , long long D ){
        long long ta = a * A ;
        long long tb = b + a * B ;
        long long tc = c * A + C ;
        long long td = d + c * B + D ;
        a = ta , b = tb , c = tc , d = td ;
 
        sum = sum + D * len + B * val ;
        val = C * len + A * val ;
    }
    void pushdown(){
        if( a == 1 && !b && !c && !d ) return ;
        ch[0]->muls( a , b , c , d ) ;
        ch[1]->muls( a , b , c , d ) ;
        a = 1 , b = c = d = 0 ;
    }
    void update(){
        val = ch[0]->val + ch[1]->val ;
        sum = ch[0]->sum + ch[1]->sum ;
    }
} *root , w[200005] , *tw = w ;
 
Node *build( int lf , int rg ){
    Node *nd = ++tw ;
    nd->a = 1 , nd->len = ( rg - lf + 1 ) ;
    if( lf != rg ){
        int mid = ( lf + rg ) >> 1 ;
        nd->ch[0] = build( lf , mid ) ;
        nd->ch[1] = build( mid+1,rg ) ;
    } return nd ;
}
 
void preWork(){
    root = build( 1 , N ) ;
    sort( q + 1 , q + Q + 1 ) ;
}
 
int L , R ; long long A , B , C , D ;
void Modify( Node *nd , int lf , int rg ){
    if( L <= lf && rg <= R ){ nd->muls( A , B , C , D ) ; return ; }
    int mid = ( lf + rg ) >> 1 ;
    nd->pushdown() ;
    if( L <= mid ) Modify( nd->ch[0] , lf , mid ) ;
    if( R >  mid ) Modify( nd->ch[1] , mid+1,rg ) ;
    nd->update() ;
}
 
long long Query( Node *nd , int lf , int rg ){
    if( L <= lf && rg <= R ) return nd->sum ;
    int mid = ( lf + rg ) >> 1 ; long long rt = 0 ;
    nd->pushdown() ;
    if( L <= mid ) rt += Query( nd->ch[0] , lf , mid ) ;
    if( R >  mid ) rt += Query( nd->ch[1] , mid+1,rg ) ;
    return rt ;
}
 
int sta[100005] , top ;
void solve(){
    for( int i = 1 , pt = 1 ; i <= N && pt <= Q ; i ++ ){
        while( top && a[ sta[top] ] >= a[i] ) top -- ;
        L = sta[top] + 1 , R = i , A = B = D = 0 , C = a[i] , Modify( root , 1 , N ) ;
        L = 1 , R = i , A = B = 1 , C = D = 0 , Modify( root , 1 , N ) ;
        sta[++top] = i ;
         
        R = i ;
        while( pt <= Q && q[pt].R == i ){
            L = q[pt].L , ans[ q[pt].id ] = Query( root , 1 , N ) ;
            pt ++ ;
        }
    } for( int i = 1 ; i <= Q ; i ++ )
        printf( "%lld\n" , ans[i] ) ;
}
 
int main(){
    scanf( "%d%d" , &N , &Q ) ;
    for( int i = 1 ; i <= N ; i ++ )
        scanf( "%d" , &a[i] ) ;
    for( int i = 1 ; i <= Q ; i ++ )
        scanf( "%d%d" , &q[i].L , &q[i].R ) , q[i].id = i ;
    preWork() ; solve() ;
}

你可能感兴趣的:(线段树)