数颜色
考场上我
我不仅没学高级数据结构,并且还傻了,写了个线段树套权值线段树。
调了一个世纪,最后因为把答案输出的地方注释掉了,丢掉了可怜的\(15pts\)。
本质是没算空间复杂度,以为这个就是正解。
结果空间炸的真实。
以后还是要时间空间复杂度都算了在写,一般\(log\)多了复杂度擦边都不太是正解。
写之前多想,为什么我考场上没想到对每个颜色开一个权值线段树?
思维不要太定势,知道是权值线段树之后,多想想下标存什么,对什么开,例如模板就是对线段树合并非常好的应用。
其实就是菜啊。
做法一:权值线段树
对每个颜色开一个权值线段树,下标为坐标。
询问就直接区间查询。
修改就分别改(改四次)。
其实想到对每个颜色开一个权值线段树之后就很简单了。
code
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <assert.h>
#define mp make_pair
#define R register int
#define int long
#define printf Ruusupuu = printf
int Ruusupuu ;
using namespace std ;
typedef long long L ;
typedef long double D ;
typedef unsigned long long G ;
typedef pair< int , int > PI ;
const signed N = 3e5 + 10 ;
inline int read(){
int w = 0 ; bool fg = 0 ; char ch = getchar() ;
while( ch < '0' || ch > '9' ) fg |= ( ch == '-' ) , ch = getchar() ;
while( ch >= '0' && ch <= '9' ) w = ( w << 1 ) + ( w << 3 ) + ( ch ^ '0' ) , ch = getchar() ;
return fg ? -w : w ;
}
inline void wap( int &a , int &b ){ a = a ^ b , b = a ^ b , a = a ^ b ; }
inline int mins( int a , int b ){ int zt = b - a ; return a + ( zt & ( zt >> 31 ) ) ; }
inline int maxs( int a , int b ){ int zt = b - a ; return b - ( zt & ( zt >> 31 ) ) ; }
inline int abss( int a ){ int zt = a >> 31 ; return ( a + zt ) ^ zt ; }
struct I{ int l , r , ls , rs , data ; } t [N << 6] ;
int n , m , knt , gnt , a [N] , root [N] , opt , lx , rx , cx , pos , delta ;
inline void ud( int x ){ t [x].data = t [t [x].ls].data + t [t [x].rs].data ; }
inline void ins( int &x , int l , int r ){
if( !x ) { x = ++ gnt , t [x].l = l , t [x].r = r ; }
if( l == r ){ t [x].data += delta ;/* if( l == 948 ) printf( "THIS IS%ld %ld\n" , delta , x ) ;*/ return ; }
int mid = ( l + r ) >> 1 ;
if( pos <= mid ) ins( t [x].ls , l , mid ) ;
else ins( t [x].rs , mid + 1 , r ) ;
ud( x ) ;
}
inline int ask( int x ){
if( !x ) return 0 ;
if( t [x].l >= lx && t [x].r <= rx ){ return t [x].data ; }
int mid = ( t [x].l + t [x].r ) >> 1 ;
int ans = 0 ;
if( lx <= mid ) ans += ask( t [x].ls ) ;
if( rx > mid ) ans += ask( t [x].rs ) ;
return ans ;
}
void sc(){
n = read() , m = read() ;
for( R i = 1 ; i <= n ; i ++ ) a [i] = read() , pos = i , delta = 1 , ins( root [a [i]] , 1 , n ) ;
// for( R i = 1 ; i <= 3 ; i ++ ) printf( "%ld\n" , root [i] ) ;
// for( R i = 1 ; i <= gnt ; i ++ ) printf( "%ld %ld %ld %ld %ld %ld\n" , i , t [i].l , t [i].r , t [i].ls , t [i].rs , t [i].data ) ;
}
void work(){
while( m -- ){
opt = read() ;
if( opt == 1 ) lx = read() , rx = read() , cx = read() , printf( "%ld\n" , ask( root [cx] ) ) ;
else{
lx = read() , //assert( lx != n ) ,
pos = lx , delta = -1 , ins( root [a [lx]] , 1 , n ) ;
pos = lx + 1 , delta = 1 , ins( root [a [lx]] , 1 , n ) ;
pos = lx , delta = 1 , ins( root [a [lx + 1]] , 1 , n ) ;
pos = lx + 1 , delta = -1 , ins( root [a [lx + 1]] , 1 , n ) ;
wap( a [lx] , a [lx + 1] ) ;
// for( R i = 1 ; i <= 3 ; i ++ ) printf( "%ld\n" , root [i] ) ;
// for( R i = 1 ; i <= gnt ; i ++ ) printf( "%ld %ld %ld %ld %ld %ld\n" , i , t [i].l , t [i].r , t [i].ls , t [i].rs , t [i].data ) ;
//for( R i = 1 ; i <= 4*n ; i ++ ) printf( "%ld %ld %ld\n" , root [i] , T [i].l , T [i].r ) ;
//for( R i = 1 ; i <= gnt ; i ++ ) printf( "%ld %ld %ld %ld %ld %ld\n" , i , t [i].l , t [i].r , t [i].ls , t [i].rs , t [i].data ) ;
}
}
}
signed main(){
sc() ;
work() ;
return 0 ;
}
做法二:在序列上二分
这个思路真的很清奇。
其实权值线段树用起来有点浪费,因为他可以改任何两个点,而不需要是相邻两个点。
那怎么用改相邻两个点呢?
先把序列以颜色为第一关键字,坐标为第二关键字排序。
询问直接二分查找。
修改:如果两个点是一种颜色,不能修改,因为会破坏他的单调性,如果不是同一种颜色,二分找到他们\(swap\)坐标就行了。
code
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <assert.h>
#define mp make_pair
#define R register int
#define int long
#define printf Ruusupuu = printf
int Ruusupuu ;
using namespace std ;
typedef long long L ;
typedef long double D ;
typedef unsigned long long G ;
typedef pair< int , int > PI ;
const int N = 3e5 + 10 ;
inline int read(){
int w = 0 ; bool fg = 0 ; char ch = getchar() ;
while( ch < '0' || ch > '9' ) fg |= ( ch == '-' ) , ch = getchar() ;
while( ch >= '0' && ch <= '9' ) w = ( w << 1 ) + ( w << 3 ) + ( ch ^ '0' ) , ch = getchar() ;
return fg ? -w : w ;
}
inline void wap( int &a , int &b ){ a = a ^ b , b = a ^ b , a = a ^ b ; }
inline int mins( int a , int b ){ int zt = b - a ; return a + ( zt & ( zt >> 31 ) ) ; }
inline int maxs( int a , int b ){ int zt = b - a ; return b - ( zt & ( zt >> 31 ) ) ; }
inline int abss( int a ){ int zt = a >> 31 ; return ( a + zt ) ^ zt ; }
int n , m , lx , rx , cx , opt , y [N] ;
struct C{ int w , index ; C(){} C( int _w , int _index ){ w = _w , index = _index ; } } a [N] ;
inline bool operator < ( C a , C b ){ return a.w == b.w ? a.index < b.index : a.w < b.w ; }
void sc(){
n = read() , m = read() ;
for( R i = 1 ; i <= n ; i ++ ) a [i].w = read() , y [i] = a [i].w , a [i].index = i ;
sort( a + 1 , a + 1 + n ) ;
// for( R i = 1 ; i <= n ; i ++ ) printf( "%ld %ld\n" , a [i].w , a [i].index ) ;
}
inline void fd(){
C aim = C( cx , lx ) ;
int ls = lower_bound( a + 1 , a + 1 + n , aim ) - a ;
aim.index = rx ;
int rs = upper_bound( a + 1 , a + 1 + n , aim ) - a - 1 ;
if( !rs ) puts( "0" ) ;
else if( ls > n ) puts( "0" ) ;
else printf( "%ld\n" , rs - ls + 1 ) ;
}
inline void ffd(){
if(y[cx]==y[cx+1])return;
C aim = C( y [cx] , cx ) ;
int ls = lower_bound( a + 1 , a + 1 + n , aim ) - a ;
aim.w = y [cx + 1] , aim.index = cx + 1 ;
int rs = lower_bound( a + 1 , a + 1 + n , aim ) - a ;
wap( a [ls].index , a [rs].index ) , wap( y [cx] , y [cx + 1] ) ;
}
void work(){
while( m -- ){
opt = read() ;
if( opt == 1 ) lx = read() , rx = read() , cx = read() , fd() ;
else cx = read() , ffd() ; //, wap( a [lx].index , a [rx].index ) , wap( y [cx] , y [cx - 1] ) ;
}
}
signed main(){
sc() ;
work() ;
return 0 ;
}
$The \ light \ has \ betrayed \ me$


浙公网安备 33010602011771号