终于把这个史前巨坑填了emmmm
BZOJ24373传送门
看题可进传送门
首先是个网格图
根据我们做 BZOJ1443 以及 BZOJ2463 的经验
我们把相邻的 黑白棋子 连边,然后跑一个最大匹配
(对于兔兔)可以发现,如果空格旁边有一个非匹配白棋子,那么挪动这个棋子之后,蛋蛋只能寻找非匹配边,然后挪动一个黑棋子。由于是最大匹配,这个黑棋子一定是匹配的,所以兔兔只需要按照最大匹配的方案,挪动黑棋子的匹配点即可
那么这个游戏会不会陷入死循环呢?答案是不会的
因为空格的路径不能相交。如果相交,路径长度一定是偶数,那么兔兔和蛋蛋都必须移动 位于交点的棋子,显然矛盾
(对于兔兔)所以,如果空格周围有一个非匹配白点,那么兔兔胜。这个条件等价于,我们把空格看成黑点,并加入匹配,空格是必匹配点(如果空格是非匹配点,那么存在一种最大匹配使得,周围四个点都是必匹配点,此时蛋蛋可以采取类似 1 1 步骤而获胜)
所以,我们只需要判断,如果 移动前和移动后 的匹配数没有变化,说明兔兔操作错了(没变化相当于,原来的点不是必匹配点)
1: 1 : 蛋蛋只需要选取一种使得周围四个点都有匹配的方案,兔兔每挪动一步,蛋蛋只需要挪动相应匹配点即可。兔兔的挪动是非匹配边,非匹配边是有限的
#include
#include
#include
using namespace std ;
char mp[45][45] ;
int N , M , K , id[45][45] , id_c , Nx , Ny , head[1605] , tp ;
struct Path{
int pre , to ;
} p[44*44*4] ;
void In( int t1 , int t2 ){
if( !t1 || !t2 ) return ;
p[++tp] = ( Path ){ head[t1] , t2 } ; head[t1] = tp ;
p[++tp] = ( Path ){ head[t2] , t1 } ; head[t2] = tp ;
}
bool vis[1605] , ban[1605] ;
int match[1605] ;
bool matching( int u ){
for( int i = head[u] ; i ; i = p[i].pre ){
int v = p[i].to ;
if( vis[v] || ban[v] ) continue ;
vis[v] = true ;
if( !match[v] || matching( match[v] ) ){
match[v] = u , match[u] = v ;
return true ;
}
} return false ;
}
void preWork(){
bool legal_B = ( Nx + Ny ) & 1 ;
for( int i = 1 ; i <= N ; i ++ )
for( int j = 1 ; j <= M ; j ++ )
if( mp[i][j] == 'O' ){
if( ( ( i + j )&1 ) != legal_B ) id[i][j] = ++id_c ;
} else if( ( ( i + j )&1 ) == legal_B ) id[i][j] = ++id_c ;
for( int i = 1 ; i <= N ; i ++ )
for( int j = 1 ; j <= M ; j ++ ) if( id[i][j] && ( i + j )&1 ){
In( id[i][j] , id[i+1][j] ) , In( id[i][j] , id[i-1][j] ) ;
In( id[i][j] , id[i][j+1] ) , In( id[i][j] , id[i][j-1] ) ;
}
for( int i = 1 ; i <= id_c ; i ++ ) if( !match[i] ){
memset( vis + 1 , false , id_c * sizeof( bool ) ) ;
matching( i ) ;
}
}
bool inM[2005] ;
void solve(){
scanf( "%d" , &K ) ;
int cnt = 0 ;
for( int i = 1 ; i <= 2 * K ; i ++ ){
int u = id[Nx][Ny] ;
ban[u] = true ;
if( match[u] ){
int v = match[u] ;
match[u] = match[v] = 0 ;
memset( vis + 1 , false , id_c * sizeof( bool ) ) ;
inM[i] = !matching( v ) ;
} scanf( "%d%d" , &Nx , &Ny ) ;
if( i % 2 == 0 && inM[i] && inM[i-1] ) cnt ++ ;
} printf( "%d\n" , cnt ) ;
for( int i = 1 ; i <= K ; i ++ )
if( inM[2*i-1] && inM[2*i] ) printf( "%d\n" , i ) ;
}
int main(){
scanf( "%d%d" , &N , &M ) ;
for( int i = 1 ; i <= N ; i ++ ){
scanf( "%s" , mp[i] + 1 ) ;
for( int j = 1 ; j <= M ; j ++ )
if( mp[i][j] == '.' ) Nx = i , Ny = j ;
} preWork() ; solve() ;
}