题解——神奇的集合

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

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

如有不足,请大佬指出

原文地址:https://www.cnblogs.com/ssw02/p/11574331.html