Loading

分组

\(k=1\)的时候不难,暴力\(32\),转化一下记录和他对立的数之前有没有就有\(40\)了。

不过有一个小技巧需要记录一下:
当要找字典序最小的\(ans\)的时候,而我们只能找到字典序最大的\(ans\),可以考虑把序列反过来跑。


\(k=2\)就比较难受了。
因为我们可以容忍一个组里面出现对立的,朴素的出现对立就重分一组肯定是不行的(不过如果你打出来也有\(70^+pts\)的好成绩)
因为我们需要满足的原则是尽量把对立的都扔到不同的组里面。
搞一个扩展域的并查集,把每一点拆成了两个点,记为\(0\)号点和\(1\)号点。
还是用\(k=1\)的思路来记录
如果之前有对立的数,把第一个点的\(0\)和第二个点的\(1\)放到一个并查集里面,把第一个点的\(1\)和第二个点的\(0\)放到一个里面
如果我们发现并查集里面有同一个节点的\(0\)\(1\),那就说明需要重新分一组了。
特判一下两个点相同的情况即可。

本人还是太菜了,这就是一个扩展域的并查集而已,但是本人却想不到。

code
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cmath>
#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 ;
const int M = 131073 ;

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 , k , st [N] , top , fen [N] , pot , a [N] ;
int fa [N << 1] , fg [N] ; bool fgg [N] ;

inline bool pd( int x ){ int y = sqrt( x ) ; if( y * y == x ) return 1 ; else return 0 ; } 

void sc(){
	n = read() , k = read() ;
	for( R i = 1 ; i <= n ; i ++ ) a [i] = read() ;
//	printf( "%ld %ld %ld %ld\n" , a [912] , a [913] , a [914] , a [915] ) ;
}

void kqo(){
	for( R i = n ; i >= 1 ; i -- ){
		bool vis = 0 ;
		for( R j = 2 ; j <= 512 ; j ++ )
			if( j * j >= a [i] && fgg [j * j - a [i]] ) { fen [++ pot] = i ; memset( fgg , 0 , sizeof( fgg ) ) ; fgg [a [i]] = 1 ; vis = 1 ; break ; }
		if( !vis ) fgg [a [i]] = 1 ;
	} printf( "%ld\n" , pot + 1 ) ;
	for( R i = pot ; i >= 1 ; i -- ) printf( "%ld " , fen [i] ) ;
}

inline int fd( int x ){
	if( x == fa [x] ) return x ;
	else return fa [x] = fd( fa [x] ) ; 
}

inline void bk(){
	for( R i = 1 ; i <= 2 * M ; i ++ ) fa [i] = i ;
}

inline bool spj( int x ){
	for( R i = 2 ; i <= 512 ; i ++ )
		if( i * i >= x && i * i != 2 * x && fg [i * i - x] ) return 0 ;
	return 1 ;
}

void kqt(){
	bk() ; 
 	for( R i = n ; i >= 1 ; i -- ){
 	//	printf( "%ld\n" , i ) ; 	
 		bool vis = 0 ;
 		for( R j = 2 ; j <= 512 ; j ++ ) if( j * j >= a [i] && fg [j * j - a [i]] ) {
 		
 			fa [fd( a [i] + M )] = fd( j * j - a [i] ) ;
	 		fa [fd( a [i] )] = fd( j * j - a [i] + M ) ;	
	 		
 			if( fd( a [i] ) == fd( a [i] + M ) ){
				if( a [i] == j * j - a [i] && fg [a [i]] == 1 && spj( a [i] ) ) break ;
				else {
 					fen [++ pot] = i ; 
 					memset( fg , 0 , sizeof( fg ) ) ; bk() ; 
 					break ;
 				}
 			}
 		} if( !vis ) 	fg [a [i]] ++ ;
 	}
	
	printf( "%ld\n" , pot + 1 ) ;
	for( R i = pot ; i >= 1 ; i -- ) printf( "%ld " , fen [i] ) ;
}

void work(){
	if( k == 1 ) kqo() ;
	else kqt() ;
}

signed main(){	
	sc() ;
	work() ;
	return 0 ;
} 
posted @ 2021-06-27 21:30  Soresen  阅读(109)  评论(0)    收藏  举报