• 题解——神奇的集合


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

    不仅让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

    数据范围与约定

    对于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
    ",ask( root[ 0 ] , 1 , N , m2 , m3 ) ) ;
    	} 
    	return 0 ;
    } 
    

    如有不足,请大佬指出

  • 相关阅读:
    MSSQL存储过程也玩“递归”
    呼之欲出 WebMail 开发手记 (二) 数据库与软件设计分析
    PageRequestManagerServerError
    呼之欲出 WebMail 开发手记 (七) 邮件发送
    呼之欲出 WebMail 开发手记 (四) 邮件收发准备
    呼之欲出 WebMail 开发手记 (六) 邮件收取
    增强 GridView 控件的功能 (二)
    VS.NET 2003 制作安装部署程序时遇到的问题
    android手机定位
    网络错误的基本处理
  • 原文地址:https://www.cnblogs.com/ssw02/p/11574331.html
Copyright © 2020-2023  润新知