题解——神奇的集合

题解——神奇的集合(动态开点+多树维护)

不仅让ssw02考场自闭,而且改的ssw02也差点自闭,改了ssw02一个停课的下午

ssw02很少写动态开点的啊


题面

Description

毒瘤的线段树操作

Input

从文件multiset.in 中读入数据。
第一行两个正整数n; q, 表示集合个数和询问数量。
接下来q 行,首先是一个整数opt:
若opt = 1,接下来三个整数l; r; x,表示向编号[l; r] 的集合中加入x。
若opt = 2,接下来两个整数l; r,表示询问编号[l; r] 的集合的元素个数和。

Output

输出到文件multiset.out 中。
对于每个询问,输出一行一个整数,表示答案。

in.1
4 4
1 1 2 1
1 1 2 2
1 1 4 1
2 1 4

out.1
10

数据范围与约定

扫描二维码关注公众号,回复: 7343976 查看本文章

对于20% 数据,n  500;m  500,数据随机。
对于另10% 数据,所有1 操作的x 均不相同。
对于另20% 数据,所有1 操作的x 均相同。
对于另20% 数据,x  20。
对于另10% 数据,1 操作的l = r。
对于100% 数据,1  n; q  200000,1  opt  2,1  l  r  n,1  x  n。

思路

思考

部分分暴力打好点可以最多拿70(ssw02意思是线段树+暴力)

如果让每个点记录加过的数,那么空间是肯定不够的。如果考虑用每一个加过的数来记录加过的区间,那么就有了思路。

我们尝试开 N 棵树来记录每个点覆盖的区间,当然不能直接建树,否则会浪费大量空间。

每一次加数操作或者查询操作都是一个区间,如果动态开点最多只会开 2logN 个点 ,空间复杂度不会超过 K NlogN ( K为一个不大的2位数的常数 )。

处理:

1.每次增加操作时,判断该区间是否被完全覆盖,否则接着分。同时还要记录是否开点(动态开点线段树基操)。
然后判断是应当加还是乘(分至一个完全覆盖区间)

if( !p )p = ++cnt ;
    int mid = ( l+r )>>1 ;
    if( l >= x && r <= y ){
        if( t[ p ].w == r-l+1 ){//判定完全覆盖
            mul( root[ 0 ] , 1 , N , l , r , 2 ) ;return ;
        }
        else if( !t[ p ].w ){//未开点(已经访问了,说明这个区间必定被覆盖过一次,只是没有下传)
            t[ p ].w = r-l+1 ;
            t[ p ].mark = true ;
            add( root[ 0 ] , 1 , N , l , r , 1 ) ;return ;
        }
        else{
            pushdown( p , l , r ) ;
            change_s( t[ p ].ls , l , mid , x , y ) ;
            change_s( t[ p ].rs , mid+1 , r , x , y ) ;
            t[ p ].w = t[ t[p].ls ].w + t[ t[p].rs ].w ;
            return ;
        }
    }
    pushdown( p , l , r ) ;
    if( x <= mid )change_s( t[ p ].ls , l , mid , x , y ) ;
    if( y > mid )change_s( t[ p ].rs , mid+1 , r , x , y ) ;
    t[ p ].w =  t[ t[p].ls ].w + t[ t[p].rs ].w ; 

2.开点下传判定(接上)

void pushdown( int p , int l , int r ){
    if( t[ p ].mark ){
        int mid = ( l+r )>>1 ;
        if( !t[p].ls )t[p].ls = ++cnt ;
        if( !t[p].rs )t[p].rs = ++cnt ;
        t[ t[p].ls ].w = mid-l+1 , t[ t[p].rs ].w = r-mid ;
        t[ t[p].ls ].mark =t[ t[p].rs ].mark = true ;
        t[ p ].mark = false ;
    }
}

3.然后就是区间加,区间乘,区间求和

void mul( int p , int l , int r , int x , int y , int val ){
    if( x <= l && r <= y ){
        t[ p ].sum = ( t[ p ].sum*val )%mod , t[ p ].pow = ( t[ p ].pow*val )%mod ;
        t[ p ].add = ( t[ p ].add*val )%mod; return ;
    }
    spread( p ) ;
    int mid = ( l+r )>>1 ;
    if( x <= mid )mul( t[ p ].ls , l , mid , x , y , val ) ;
    if( y > mid )mul( t[ p ].rs , mid+1 , r , x , y , val ) ;
    //if( wrong == 5 )cout<<t[ 753 ].sum<<" "<<val<<endl ;
    t[ p ].sum = t[ t[p].ls ].sum + t[ t[p].rs].sum ;
}
void  add( int p , int l , int r , int x , int y , int val ){
    if( x <= l  && r <= y ){
        t[ p ].sum = ( t[ p ].sum + ( t[ p ].r - t[ p ].l + 1 )*val )%mod ;
        t[ p ].add = ( t[ p ].add+val )%mod ; return ;
    }
    spread( p ) ;
    int mid = ( l+r )>>1 ;
    if( x <= mid )add( t[ p ].ls , l , mid , x , y , val ) ;
    if( y > mid )add( t[ p ].rs , mid+1 , r , x , y , val ) ; 
    t[ p ].sum = t[ t[p].ls ].sum + t[ t[p].rs ].sum ;
}

AC code

#include<bits/stdc++.h>
using namespace std ;
const  int  MAXN = 200005 ;
const long long mod = 998244353 ;
#define ll long long
inline int read(){
    int s=0 ; char g=getchar() ; while( g>'9'||g<'0' )g=getchar() ;
    while( g>='0'&&g<='9' )s=s*10+g-'0',g=getchar(); return s ;
}
struct Seg{
    int ls , rs , l , r ;
    ll sum , add , pow , w ;
    bool mark ;
}t[ MAXN*30 ];
int N , Q , cnt = 0 , root[ MAXN ] , wrong = 0 ;
void  build( int &p , int l , int r ){
    p = ++cnt , t[ p ].pow = 1 ;
    t[ p ].l = l , t[ p ].r = r ;
    if( l == r ){
        t[ p ].pow = 1  ; return ; 
    }
    int mid = ( l+r )>>1 ;
    build( t[ p ].ls , l , mid ) , build( t[ p ].rs , mid+1 , r ) ;
}
void  spread( int p ){
    int l = t[ p ].ls , r = t[ p ].rs ;
    if( t[ p ].pow  ){
        t[ l ].sum = ( t[ l ].sum*t[ p ].pow )%mod , t[ r ].sum = ( t[ r ].sum*t[ p ].pow )%mod ;
        t[ l ].add = ( t[ l ].add*t[ p ].pow )%mod , t[ r ].add = ( t[ r ].add*t[ p ].pow )%mod ; 
        t[ l ].pow = ( t[ l ].pow*t[ p ].pow )%mod , t[ r ].pow = ( t[ r ].pow*t[ p ].pow )%mod ;
        t[ p ].pow = 1 ;
    }
    if( t[ p ].add ){
        t[ l ].sum = ( t[ l ].sum + ( t[ l ].r - t[ l ].l + 1)*t[ p ].add )%mod ;
        t[ r ].sum = ( t[ r ].sum + ( t[ r ].r - t[ r ].l + 1)*t[ p ].add )%mod ;
        t[ l ].add =(  t[ p ].add + t[ l ].add )%mod , t[ r ].add = ( t[ p ].add + t[ r ].add )%mod ;
        t[ p ].add = 0 ;
    }
}
void mul( int p , int l , int r , int x , int y , int val ){
    if( x <= l && r <= y ){
        t[ p ].sum = ( t[ p ].sum*val )%mod , t[ p ].pow = ( t[ p ].pow*val )%mod ;
        t[ p ].add = ( t[ p ].add*val )%mod; return ;
    }
    spread( p ) ;
    int mid = ( l+r )>>1 ;
    if( x <= mid )mul( t[ p ].ls , l , mid , x , y , val ) ;
    if( y > mid )mul( t[ p ].rs , mid+1 , r , x , y , val ) ;
    //if( wrong == 5 )cout<<t[ 753 ].sum<<" "<<val<<endl ;
    t[ p ].sum = t[ t[p].ls ].sum + t[ t[p].rs].sum ;
}
void  add( int p , int l , int r , int x , int y , int val ){
    if( x <= l  && r <= y ){
        t[ p ].sum = ( t[ p ].sum + ( t[ p ].r - t[ p ].l + 1 )*val )%mod ;
        t[ p ].add = ( t[ p ].add+val )%mod ; return ;
    }
    spread( p ) ;
    int mid = ( l+r )>>1 ;
    if( x <= mid )add( t[ p ].ls , l , mid , x , y , val ) ;
    if( y > mid )add( t[ p ].rs , mid+1 , r , x , y , val ) ; 
    t[ p ].sum = t[ t[p].ls ].sum + t[ t[p].rs ].sum ;
}
void pushdown( int p , int l , int r ){
    if( t[ p ].mark ){
        int mid = ( l+r )>>1 ;
        if( !t[p].ls )t[p].ls = ++cnt ;
        if( !t[p].rs )t[p].rs = ++cnt ;
        t[ t[p].ls ].w = mid-l+1 , t[ t[p].rs ].w = r-mid ;
        t[ t[p].ls ].mark =t[ t[p].rs ].mark = true ;
        t[ p ].mark = false ;
    }
}
void  change_s( int &p , int l , int r , int x , int y ){
    if( !p )p = ++cnt ;
    int mid = ( l+r )>>1 ;
    if( l >= x && r <= y ){
        if( t[ p ].w == r-l+1 ){
            mul( root[ 0 ] , 1 , N , l , r , 2 ) ;return ;
        }
        else if( !t[ p ].w ){
            t[ p ].w = r-l+1 ;
            t[ p ].mark = true ;
            add( root[ 0 ] , 1 , N , l , r , 1 ) ;return ;
        }
        else{
            pushdown( p , l , r ) ;
            change_s( t[ p ].ls , l , mid , x , y ) ;
            change_s( t[ p ].rs , mid+1 , r , x , y ) ;
            t[ p ].w = t[ t[p].ls ].w + t[ t[p].rs ].w ;
            return ;
        }
    }
    pushdown( p , l , r ) ;
    if( x <= mid )change_s( t[ p ].ls , l , mid , x , y ) ;
    if( y > mid )change_s( t[ p ].rs , mid+1 , r , x , y ) ;
    t[ p ].w =  t[ t[p].ls ].w + t[ t[p].rs ].w ; 
}
ll  ask( int p , int l , int r , int x , int y ){
    if( x <= l && y >= r )return (ll)t[ p ].sum%mod ;
    spread( p ) ;
    int mid = (l+r)>>1 ;
    ll val = 0 ;
    if( x <= mid )val = ask( t[p].ls , l , mid , x , y )%mod ;
    if( y > mid )val = ( val + ask( t[p].rs , mid+1 , r , x , y ) )%mod ;
    return val ;
}
void  check( int p , int l , int r ){
    cout<<p<<" "<<t[ p ].sum<<endl ;
    int mid = ( l+r )>>1 ;
    if( t[ p ].ls )check( t[p].ls , l , mid ) ;
    if( t[ p ].rs )check( t[p].rs , mid+1 , r ) ;
}
int main(){
    freopen("multiset.in","r",stdin);
    freopen("multiset.out","w",stdout);
    N = read() , Q = read() ; int m1 , m2 , m3 , m4 ;
    for( int i = 1 ; i <= N ; ++i )root[ i ] = ++cnt ;//颜色首节点
    build( root[ 0 ] , 1 , N ) ;
    while( Q-- ){
        m1 = read() , m2 = read() , m3 = read() ;
        if( m1 == 1 ){
            m4 = read() ; change_s( root[ m4 ] , 1 , N , m2 , m3 ) ; 
        }
        else printf("%lld\n",ask( root[ 0 ] , 1 , N , m2 , m3 ) ) ;
    } 
    return 0 ;
} 

如有不足,请大佬指出

猜你喜欢

转载自www.cnblogs.com/ssw02/p/11574331.html
今日推荐