Loading

数颜色

考场上我

image

我不仅没学高级数据结构,并且还傻了,写了个线段树套权值线段树。
调了一个世纪,最后因为把答案输出的地方注释掉了,丢掉了可怜的\(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 ;
} 
posted @ 2021-06-27 21:26  Soresen  阅读(103)  评论(0)    收藏  举报