Dancing links X
这个算法主要依据于一个高端数据结构:十字交叉双向循环链表
重复覆盖的话还需要一个\(IDA*\),主要是数据结构优化一些常数吧,复杂度要被卡还是跟爆搜一样,应该是个骗分用的算法吧(\(口胡ing\))
Upd 4.28:学完重复覆盖了,DLX并不是很适合重复覆盖,因为需要迭代加深,导致他在\(1s\)内无法处理出来超过\(100\)列,超过一百列之后远远比状压慢,所以,我本来是为了优化愤怒的小鸟才学的这个,结果学玩之后并没有做出来愤怒的小鸟(愤怒)
精准覆盖(\(N ,M <= 500\)适用)
#include <cstdio>
#include <cstring>
#include <algorithm>
#define R register int
using namespace std ;
typedef long long L ;
typedef double D ;
const int N = 5e2 + 10 ;
const int S = 5e3 + 10 + N ; //数据极限的话由于要存表头,所以需要多开
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 ;
}
int n , m ;
struct DLX{
int n , m ; // n 行 m 列
int size ; // 一共size个1
int l [S] , r [S] , u [S] , d [S] , row [S] , col [S] ; //点的基本六个元素 up,down,left,right指针,点属于第row行第col列
int head [N] , cvr [N] ; //记录行的选择(这一行的最后一个元素来实现加点),列的覆盖(cvr)
int ans [N] ;// 答案栈
void pre( int ns , int ms ){
n = ns , m = ms ;
for( R i = 0 ; i <= m ; i ++ ){ //初始化表头
cvr [i] = 0 ;
u [i] = d [i] = i ;
l [i] = i - 1 , r [i] = i + 1 ;
} // 横向连接,纵向是空的
r [m] = 0 ; l [0] = m ; // 循环链表
size = m ; // 已有m个表头节点
for( R i = 1 ; i <= n ; i ++ )
head [i] = -1 ;
}
void ins( int roww , int coll ){ // 插入一个点
size ++ ; cvr [coll] ++ ; // 加入一个点,列的覆盖情况情况更改
col [size] = coll , row [size] = roww ;
// printf( "THIS IS%d\n" , size ) ;
// printf( "F Down%5d UP%5d U [D]%5d head%5d r [head]%5d l [r]%5d\n" , d [coll] , u [coll] , u [d [coll]] , head [roww] , r [head [roww]] , l [head [roww]] ) ;
d [size] = d [coll] ;
u [d [coll]] = size ;
u [size] = coll ;
d [coll] = size ;
if( head [roww] == -1 ) //如果这一行没有点,初始化这一行
head [roww] = l [size] = r [size] = size ;
else{
r [size] = r [head [roww]] ;
l [r [head [roww]]] = size ;
l [size] = head [roww] ;
r [head [roww]] = size ;
}
/// printf( "F Down%5d UP%5d U [D]%5d head%5d r [head]%5d l [r]%5d\n" , d [coll] , u [coll] , u [d [coll]] , head [roww] , r [head [roww]] , l [head [roww]] ) ;
// printf( "U %5d D %5d L %5d R %5d\n" , u [size] , d [size] , l [size] , r [size] ) ;
// printf( "U %5d D %5d L %5d R %5d\n" , u [size - 1] , d [size - 1] , l [size - 1] , r [size - 1] ) ;
}
void del( int c ){ //删掉第c列
l [r [c]] = l [c] ;
r [l [c]] = r [c] ;
for( R i = d [c] ; i != c ; i = d [i] ) // ,枚举这一列的所有节点
for( R j = r [i] ; j != i ; j = r [j] ) // 枚举这个节点所在的一行的所有节点
u [d [j]] = u [j] , d [u [j]] = d [j] , cvr [col [j]] -- ; //删除之
}
void bak( int c ){ // 复原第c列
for( R i = u [c] ; i != c ; i = u [i] )
for( R j = l [i] ; j != i ; j = l [j] )
d [u [j]] = j , u [d [j]] = j , cvr [col [j]] ++ ;
l [r [c]] = r [l [c]] = c ;
}
bool dance( int dep ){ // 已经搞定dep行
if( r [0] == 0 ){ //全部覆盖
for( R i = 0 ; i < dep ; i ++ )
printf( "%d " , ans [i] ) ;
return 1 ;
}
int c = r [0] ; // 指向第一列
for( R i = r [0] ; i != 0 ; i = r [i] ) //表头指针枚举
if( cvr [i] < cvr [c] ) c = i ; // 选择列中元素最少的操作
del( c ) ;
for( R i = d [c] ; i != c ; i = d [i] ){ //枚举这一列
ans [dep] = row [i] ; // 压入答案栈
for( R j = r [i] ; j != i ; j = r [j] )
del( col [j] ) ; //诛这一行里元素的九族
if( dance( dep + 1 ) ) return 1 ;
for( R j = l [i] ; j != i ; j = l [j] )
bak( col [j] ) ; //冤杀,再请回来
}
bak( c ) ; // 无法继续加深,回溯
return 0 ;
}
} a ;
void sc(){
n = read() , m = read() ; a.pre( n , m ) ;
for( R i = 1 ; i <= n ; i ++ )
for( R j = 1 ; j <= m ; j ++ )
if( read() ) a.ins( i , j ) ;
}
void work(){
if( !a.dance( 0 ) ) puts( "No Solution!" ) ;
else return ;
}
signed main(){
sc() ;
work() ;
return 0 ;
}
重复覆盖(\(N, M <= 100\)适用)
#include <cstdio>
#include <cstring>
#include <algorithm>
#define R register int
using namespace std ;
typedef long long L ;
typedef double D ;
const int N = 5e2 + 10 ;
const int S = 5e3 + 10 + N ; //数据极限的话由于要存表头,所以需要多开
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 ;
}
int n , m , dep ;
struct DLX{
int n , m ; // n 行 m 列
int size ; // 一共size个1
int l [S] , r [S] , u [S] , d [S] , row [S] , col [S] ; //点的基本六个元素 up,down,left,right指针,点属于第row行第col列
int head [N] , cvr [N] ; //记录行的选择(这一行的最后一个元素来实现加点),列的覆盖(cvr)
int ans [N] , top ;// 答案栈
bool fg [N] ; //估价用
void pre( int ns , int ms ){
n = ns , m = ms ;
for( R i = 0 ; i <= m ; i ++ ){ //初始化表头
cvr [i] = 0 ;
u [i] = d [i] = i ;
l [i] = i - 1 , r [i] = i + 1 ;
} // 横向连接,纵向是空的
r [m] = 0 ; l [0] = m ; // 循环链表
size = m ; // 已有m个表头节点
for( R i = 1 ; i <= n ; i ++ )
head [i] = -1 ;
}
void ins( int roww , int coll ){ // 插入一个点
size ++ ; cvr [coll] ++ ; // 加入一个点,列的覆盖情况情况更改
col [size] = coll , row [size] = roww ;
// printf( "THIS IS%d\n" , size ) ;
// printf( "F Down%5d UP%5d U [D]%5d head%5d r [head]%5d l [r]%5d\n" , d [coll] , u [coll] , u [d [coll]] , head [roww] , r [head [roww]] , l [head [roww]] ) ;
d [size] = d [coll] ;
u [d [coll]] = size ;
u [size] = coll ;
d [coll] = size ;
if( head [roww] == -1 ) //如果这一行没有点,初始化这一行
head [roww] = l [size] = r [size] = size ;
else{
r [size] = r [head [roww]] ;
l [r [head [roww]]] = size ;
l [size] = head [roww] ;
r [head [roww]] = size ;
}
/// printf( "F Down%5d UP%5d U [D]%5d head%5d r [head]%5d l [r]%5d\n" , d [coll] , u [coll] , u [d [coll]] , head [roww] , r [head [roww]] , l [head [roww]] ) ;
// printf( "U %5d D %5d L %5d R %5d\n" , u [size] , d [size] , l [size] , r [size] ) ;
// printf( "U %5d D %5d L %5d R %5d\n" , u [size - 1] , d [size - 1] , l [size - 1] , r [size - 1] ) ;
}
void del( int c ){ //删掉元素所在的列
for( R i = d [c] ; i != c ; i = d [i] ) // ,枚举这一列的所有节点
l [r [i]] = l [i] , r [l [i]] = r [i] ;
}
void bak( int c ){ // 复原元素所在的列
for( R i = u [c] ; i != c ; i = u [i] )
l [r [i]] = i , r [l [i]] = i ;
}
inline int guess(){ //估价函数
//强剪枝。这个 剪枝利用的思想是A*搜索中的估价函数。
//即,对于当前的递归深度K下的矩阵,估计其最好情况下(即最少还需要多少步)才能出解。
//也就是,如果将能够覆盖当 前列的所有行全部选中,去掉这些行能够覆盖到的列,将这个操作作为一层深度。
//重复此操作直到所有列全部出解的深度是多少。如果当前深度加上这个估价函数返 回值,其和已然不能更优(也就是已经超过当前最优解),则直接返回,不必再搜。
memset( fg , 0 , sizeof ( fg ) ) ;
int cnt = 0 ;
for( R i = r [0] ; i ; i = r [i] ){
if( fg [i] ) continue ;
cnt ++ ;
for( R j = d [i] ; j != i ; j = d [j] )
for( R k = r [j] ; k != j ; k = r [k] )
fg [col [k]] = 1 ;
}
return cnt ;
}
/*重复覆盖
1、如果矩阵为空,得到结果,返回
2、从矩阵中选择一列,以选取最少元素的列为优化方式
3、删除该列及其覆盖的行
4、对该列的每一行元素:删除一行及其覆盖的列,
5、进行下一层搜索,如果成功则返回
6、恢复现场,跳至4
7、恢复所选择行
*/
//以上注释均转载于第一个链接
bool dance( ){
// printf( "GUESS%d TOP%d DEP%d\n" , guess() , top , dep ) ;
if( guess() + top > dep ) return 0 ;
if( r [0] == 0 ) return 1 ;
int c = r [0] ; // 指向第一列
for( R i = r [0] ; i != 0 ; i = r [i] ) //表头指针枚举
if( cvr [i] < cvr [c] ) c = i ; // 选择列中元素最少的操作
// if( !cvr [c] ) return 0 ;
for( R i = d [c] ; i != c ; i = d [i] ){ //枚举这一列
ans [++ top] = row [i] ; // 压入答案栈
del ( i ) ;
for( R j = r [i] ; j != i ; j = r [j] ) del( j ) ;
if( dance() ) return 1 ;
for( R j = l [i] ; j != i ; j = l [j] ) bak( j ) ;
bak( i ) ;
top -- ;
}
return 0 ;
}
} a ;
void sc(){
n = read() , m = read() ; a.pre( n , m ) ;
for( R i = 1 ; i <= n ; i ++ )
for( R j = 1 ; j <= m ; j ++ )
if( read() ) a.ins( i , j ) ;
}
void work(){
dep = 1 ;
while( !a.dance() ) /* printf( "%d\n" , dep ) ,*/ dep ++ ; // 迭代加深
printf( "%d\n" , dep ) ;
for( R i = 1 ; i <= dep ; i ++ ) printf( "%d " , a.ans [i] ) ;
}
signed main(){
sc() ;
work() ;
return 0 ;
}
愤怒的小鸟
这个题预处理出来抛物线之后(最多不会超过\(n*n/2\)条,如果开数组记录的话还可以更少,只不过预处理也要花些时间)就是一个重复覆盖问题,打个板子就行了
Upd;4/28:太寂寞了,发现所有抛物线都处理出来有200多条(极限数据),即使用哈希去重也有100多条,\(T=5\)实测\(4.5s\)跑出来,决定用这个算法之前一定要看好数据范围,超过\(50\)就慎用了
还是说一下思路吧:预处理出来每一条抛物线能砸多少鸟,最后转化为重复覆盖问题用状压,对于每一个状态\(i\)枚举\(i|j,j\in抛物线\),取最小值就行了
状压正解
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <bitset>
#include <cmath>
#include <iostream>
//#include <queue>
#define R register int
//#define int long long
using namespace std ;
typedef long long L ;
typedef double D ;
const int N = 20 ;
const int iinf = 0x7fffffff ;
const int S = 1e7 ;
const D li = 1e-8 ;
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 ;
}
int top , pot ;
int t , n , lg [1 << N] , f [1 << N] , rks [N][N] ;
struct BIRD{ D x , y ; } a [N] ;
//struct OP{ D x , y ; int cnt [1 << N] ; } f [N] ;
inline void debug( int x ) { cout << bitset <11> ( x ) << endl ; }
inline int lb( int x ){ return x & -x ; }
void sc(){
t = read() ;
}
// 1 3 0 1.11 1.41 2.34 1.79 2.98 1.49
// 1 2 0 1.41 2.00 1.73 3.00
inline D geta( D x , D y , D xx , D yy ){
D a = x * yy - xx * y ;
D b = x * xx * ( xx - x ) ;
return ( a / b ) ;
}
inline D getb( D x , D y , D xx , D yy ){
D a = x * x * yy - xx * xx * y ;
D b = x * xx * ( x - xx ) ;
return ( a / b ) ;
}
inline bool fc( D a , D b , D x , D y ){
if( fabs( a * x * x + b * x - y ) < li ) return 1 ;
else return 0 ;
}
void work(){
while( t -- ){
n = read() , read() ;
for( R i = 0 ; i < n ; i ++ ) scanf( "%lf%lf" , & a [i].x , & a [i].y ) ;
memset( f , 0x3f , sizeof( f ) ) ; f [0] = 0 ; pot = 0 ;
memset( rks , 0 , sizeof( rks ) ) ;
for( R i = 0 ; i < n ; i ++ ){
rks [i][i] = 1 << i ;
for( R j = 0 ; j < n ; j ++ ){
if( fabs( a [i].x - a [j].x ) < li ) continue ;
D as = geta( a [i].x , a [i].y , a [j].x , a [j].y ) ;
if( as >= 0 ) continue ;
D bs = getb( a [i].x , a [i].y , a [j].x , a [j].y ) ;
// printf( "%d %d %lf %lf\n" , i , j , as , bs ) ;
for( R k = 0 ; k < n ; k ++ ){
if( fc( as , bs , a [k].x , a [k].y ) )
rks [i][j] += 1 << k ;
}
}
}
for( R i = 0 ; i < ( ( 1 << n ) - 1 ) ; i ++ )
for( R j = 0 ; j < n ; j ++ )
for( R js = 0 ; js < n ; js ++ )
f [i | rks [j][js]] = min( f [i | rks [j][js]] , f [i] + 1 ) ;
printf( "%d\n" , f [( 1 << n ) - 1] ) ;
}
}
signed main(){
sc() ;
work() ;
return 0 ;
}
DLX哈希优化TLE90分
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <vector>
#define R register int
using namespace std ;
typedef long long L ;
typedef double D ;
typedef unsigned long long G ;
const int N = 4e2 + 10 ;
const int H = 20 ;
const int S = 2e4 + 10 + N ; //数据极限的话由于要存表头,所以需要多开
const D li = 1e-8 ;
const int P = 131 ;
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 ;
}
int nss , dep , uu ;
bool fggg , fgg [N] ;
int T ; int rks [N][H] ;
vector<G> sb ;
G Has [N] ; //哈希去重剪枝
struct DLX{
int n , m ; // n 行 m 列
int size ; // 一共size个1
int l [S] , r [S] , u [S] , d [S] , row [S] , col [S] ; //点的基本六个元素 up,down,left,right指针,点属于第row行第col列
int head [N] , cvr [H] ; //记录行的选择(这一行的最后一个元素来实现加点),列的覆盖(cvr)
int ans [N] , top ;// 答案栈
bool fg [N] ; //估价用
void clear(){
n = 0 ; m = 0 ; size = 0 ; top = 0 ;
memset( l , 0 , sizeof( l ) ) ;
memset( r , 0 , sizeof( l ) ) ;
memset( u , 0 , sizeof( l ) ) ;
memset( d , 0 , sizeof( l ) ) ;
memset( row , 0 , sizeof( l ) ) ;
memset( col , 0 , sizeof( l ) ) ;
memset( head , 0 , sizeof( head ) ) ;
memset( cvr , 0 , sizeof( cvr ) ) ;
memset( ans , 0 , sizeof( ans ) ) ;
}
void pre( int ns , int ms ){
// printf( "%d\n" , ms ) ;
n = ns , m = ms ;
for( R i = 0 ; i <= m ; i ++ ){ //初始化表头
cvr [i] = 0 ;
u [i] = d [i] = i ;
l [i] = i - 1 , r [i] = i + 1 ;
} // 横向连接,纵向是空的
r [m] = 0 ; l [0] = m ; // 循环链表
size = m ; // 已有m个表头节点
for( R i = 1 ; i <= n ; i ++ )
head [i] = -1 ;
}
void ins( int roww , int coll ){ // 插入一个点
// printf( "INS%d %d\n" , roww , coll ) ;
size ++ ; cvr [coll] ++ ; // 加入一个点,列的覆盖情况情况更改
col [size] = coll , row [size] = roww ;
// printf( "THIS IS%d\n" , size ) ;
// printf( "F Down%5d UP%5d U [D]%5d head%5d r [head]%5d l [r]%5d\n" , d [coll] , u [coll] , u [d [coll]] , head [roww] , r [head [roww]] , l [head [roww]] ) ;
d [size] = d [coll] ;
u [d [coll]] = size ;
u [size] = coll ;
d [coll] = size ;
if( head [roww] == -1 ) //如果这一行没有点,初始化这一行
head [roww] = l [size] = r [size] = size ;
else{
r [size] = r [head [roww]] ;
l [r [head [roww]]] = size ;
l [size] = head [roww] ;
r [head [roww]] = size ;
}
/// printf( "F Down%5d UP%5d U [D]%5d head%5d r [head]%5d l [r]%5d\n" , d [coll] , u [coll] , u [d [coll]] , head [roww] , r [head [roww]] , l [head [roww]] ) ;
// printf( "U %5d D %5d L %5d R %5d\n" , u [size] , d [size] , l [size] , r [size] ) ;
// printf( "U %5d D %5d L %5d R %5d\n" , u [size - 1] , d [size - 1] , l [size - 1] , r [size - 1] ) ;
}
void del( int c ){ //删掉元素所在的列
for( R i = d [c] ; i != c ; i = d [i] ) // ,枚举这一列的所有节点
l [r [i]] = l [i] , r [l [i]] = r [i] ;
}
void bak( int c ){ // 复原元素所在的列
for( R i = u [c] ; i != c ; i = u [i] )
l [r [i]] = i , r [l [i]] = i ;
}
inline int guess(){ //估价函数
//强剪枝。这个 剪枝利用的思想是A*搜索中的估价函数。
//即,对于当前的递归深度K下的矩阵,估计其最好情况下(即最少还需要多少步)才能出解。
//也就是,如果将能够覆盖当 前列的所有行全部选中,去掉这些行能够覆盖到的列,将这个操作作为一层深度。
//重复此操作直到所有列全部出解的深度是多少。如果当前深度加上这个估价函数返 回值,其和已然不能更优(也就是已经超过当前最优解),则直接返回,不必再搜。
memset( fg , 0 , sizeof ( fg ) ) ;
int cnt = 0 ;
for( R i = r [0] ; i ; i = r [i] ){
if( fg [i] ) continue ;
cnt ++ ;
for( R j = d [i] ; j != i ; j = d [j] )
for( R k = r [j] ; k != j ; k = r [k] )
fg [col [k]] = 1 ;
}
return cnt ;
}
/*重复覆盖
1、如果矩阵为空,得到结果,返回
2、从矩阵中选择一列,以选取最少元素的列为优化方式
3、删除该列及其覆盖的行
4、对该列的每一行元素:删除一行及其覆盖的列,
5、进行下一层搜索,如果成功则返回
6、恢复现场,跳至4
7、恢复所选择行
*/
//以上注释均转载于第一个链接
bool dance( ){
// printf( "GUESS%d TOP%d DEP%d\n" , guess() , top , dep ) ;
if( guess() + top > dep ) return 0 ;
if( r [0] == 0 ) return 1 ;
int c = r [0] ; // 指向第一列
for( R i = r [0] ; i != 0 ; i = r [i] ) //表头指针枚举
if( cvr [i] < cvr [c] ) c = i ; // 选择列中元素最少的操作
// printf( "C%d %d\n" , c , cvr [c] ) ;
if( !cvr [c] ) return 0 ;
for( R i = d [c] ; i != c ; i = d [i] ){ //枚举这一列
ans [++ top] = row [i] ; // 压入答案栈
// printf( "PASS%d %d %d\n" , dep , top , i ) ;
del ( i ) ;
for( R j = r [i] ; j != i ; j = r [j] ) del( j ) ;
if( dance() ) return 1 ;
for( R j = l [i] ; j != i ; j = l [j] ) bak( j ) ;
bak( i ) ;
top -- ;
}
return 0 ;
}
} a ;
struct POINT{ D x , y ; } p [N] ;
void sc(){
T = read() ;
}
inline D geta( D x , D y , D xx , D yy ){
D a = x * yy - xx * y ;
D b = x * xx * ( xx - x ) ;
return ( a / b ) ;
}
inline D getb( D x , D y , D xx , D yy ){
D a = x * x * yy - xx * xx * y ;
D b = x * xx * ( x - xx ) ;
return ( a / b ) ;
}
inline bool fc( D a , D b , D x , D y ) {
if( fabs( a * x * x + b * x - y ) < li ) return 1 ;
else return 0 ;
}
inline G qpow( int p ){
int ans = 1 ; int base = P ;
while( p){
if( p & 1 ) ans *= base ;
base *= P ;
p >>= 1 ;
} return ans ;
}
void debug(){
printf( "UU%d\n" , uu ) ;
for( R i = 1 ; i <= uu ; i ++ ){
printf( "\nI%d\n" , i ) ;
for( R j = 1 ; j <= rks [i][0] ; j ++ )
printf( "%d " , rks [i][j] ) ;
}
}
inline bool check( int p ){
for( R i = 0 ; i < sb.size() ; i ++ )
if( Has [p] == sb [i] ) return 1 ;
return 0 ;
}
void mk(){
fggg = 0 ; memset( fgg , 0 , sizeof( fgg ) ) ; sb.clear() ;
memset( Has , 0 , sizeof( Has ) ) ;
a.clear() ; memset( rks , 0 , sizeof( rks ) ) ; uu = 0 ;
nss = read() ; read() ;
for( R i = 1 ; i <= nss ; i ++ ) scanf( "%lf%lf" , & p [i].x , & p [i].y ) ;
for( R i = 1 ; i <= nss ; i ++ ){
rks [++ uu][++ rks [uu][0]] = i ;
for( R j = 1 ; j <= nss ; j ++ ){
if( i == j ) continue ;
if( fabs( p [i].x - p [j].x ) < li ) continue ;
D as = geta( p [i].x , p [i].y , p [j].x , p [j].y ) ;
if( as >= 0 ) continue ;
else rks [++ uu][++ rks [uu][0]] = i ;
D bs = getb( p [i].x , p [i].y , p [j].x , p [j].y ) ;
// printf( "%d %d %lf %lf\n" , i , j , as , bs ) ;
rks [uu][++ rks [uu][0]] = j ;
// printf( "IJ%d %d\n" , i , j ) ;
for( R k = 1 ; k <= nss ; k ++ ){
if( k == i || k == j ) continue ;
if( fc( as , bs , p [k].x , p [k].y ) ) { //printf( "K%d\n" , k ) ;
rks [uu][++ rks [uu][0]] = k ; }
}
}
}
// debug() ;
int uuu = uu ;
// printf( "%d\n" , uu ) ;
for( R i = 1 ; i <= uu ; i ++ ){
sort( rks [i] + 1 , rks [i] + 1 + rks [i][0] ) ;
for( R j = 1 ; j <= rks [i][0] ; j ++ )
Has [i] += rks [i][j] * qpow( j ) ;
// printf( "%llu\n" , Has [i] ) ;
}
for( R i = 1 ; i <= uu ; i ++ ){
if( !check( i ) ) sb.push_back( Has [i] ) ;
else fgg [i] = 1 , uuu -- ;
}
// for( R i = 1 ; i <= uu ; i ++ ) cout << i << " " << fgg [i] << endl ;
// printf( "UUU%d\n" , uuu ) ;
if( !uu ) { fggg = 1 ; return ; }
a.pre( uuu , nss ) ;
int uuuu = 0 ;
for( R i = 1 ; i <= uu ; i ++ ){
if( fgg [i] ) { /* printf( "%d\n" , i ) ; */ continue ; }
uuuu ++ ;
for( R j = 1 ; j <= rks [i][0] ; j ++ ){
a.ins( uuuu , rks [i][j] ) ;
}
}
}
void work(){
while( T -- ){
mk() ; if( fggg ){ printf( "%d\n" , nss ) ; continue ; }
dep = 1 ;
while( !a.dance() ) /*printf( "DEP%d\n" , dep ) , */ dep ++ ; // 迭代加深
printf( "%d\n" , dep ) ;
}
}
signed main(){
sc() ;
work() ;
return 0 ;
}
哀悼因寂寞优化逝去的三天
$The \ light \ has \ betrayed \ me$

浙公网安备 33010602011771号