题解——神奇的集合(动态开点+多树维护)
不仅让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 ;
}