P7479

· · 题解

\textbf {P7479 [B]至曾是英雄的您}

业余五段来了

想不到我一个业余五段竟然被两个业余四段出的题卡了好久才过...

作为一个小学的时候(也好久不下了)下了六年围棋的选手,我上来看到这题的第一反应就是找眼。什么是眼呢?概念我也忘了就是指由黑棋与边界围成的范围(白棋不能把里面填满;黑棋与黑棋围起来的也行)

比如下面这几个图形中黑棋围起来的中间的部分就是眼

2 3
*.*
***
3 3
***
*.*
***
3 3
***
*.*
**.
3 4
****
*..*
****

知道了眼的概念以后,我们发现当黑棋与白棋交替下棋的时候,如果这块黑棋又两颗眼,那么白棋是不能杀掉黑棋的。可是我们发现这种方法马上被hack了——因为白棋是可以连续下的。看下面这个棋盘

7 5
*******
****.**
*.*...*
****.**
*******

这里黑棋有两颗眼,但是右边的梅花五中白棋可以把边界填上,在下一个棋子放入左边的眼中,黑棋就死了。

于是我又想到了一种方法,不考虑白棋能不能下,用白棋把黑棋的气都填上(如果白棋下的地方不是黑棋的气,那就对黑棋没有威胁了)。最后,如果白棋的方案是合法的,那黑棋一定是死的,否则是活的。

#include <iostream>
#include <cstring>
#include <cstdio>

#define int long long

#define rep(i,a,b) for(register int i(a);i^(b+1);++i)

/***************快读***************/

namespace IO {
char buf[1<<21], *p1 = buf, *p2 = buf, buf1[1<<21];
inline char gc () {return p1 == p2 && (p2 = (p1 = buf) + fread (buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++;}

#ifndef ONLINE_JUDGE
#endif

#define gc getchar

template<class I>
inline void read(I &x) {
    x = 0; I f = 1; char c = gc();
    while(c < '0' || c > '9') {if(c == '-') f = -1; c = gc(); }
    while(c >= '0' && c <= '9') {x = x * 10 + c - '0'; c = gc(); }
    x *= f;
}

template<class I>
inline void write(I x) {
    if(x == 0) {putchar('0'); return;}
    I tmp = x > 0 ? x : -x;
    if(x < 0) putchar('-');
    int cnt = 0;
    while(tmp > 0) {
        buf1[cnt++] = tmp % 10 + '0';
        tmp /= 10;
    }
    while(cnt > 0) putchar(buf1[--cnt]);
}

#define in(x) read(x)
#define outn(x) write(x), putchar('\n')
#define out(x) write(x), putchar(' ')

} using namespace IO;

/***************快读***************/

#define maxn 5010

int t,n,m;
char map1[maxn][maxn];
bool vis[maxn][maxn];
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};

//dfs求白棋的气判断死活
inline bool dfs(register int x,register int y){
    vis[x][y]=1;
    register bool flag=false;
    for(register int i=0;i^4;++i){
        register int NewX=x+dx[i];
        register int NewY=y+dy[i];
        if(map1[NewX][NewY]=='.'){
            flag=1;
        }
        if(NewX>0&&NewY>0&&NewX<=n&&NewY<=m&&!vis[NewX][NewY]&&map1[NewX][NewY]=='@'){
            flag|=dfs(NewX,NewY);
        }
    }
    return flag;
}

signed main(){
    read(t);
    while(t--){
        read(n),read(m);
        for(register int i=0;i<=n+1;++i){
            for(register int j=0;j<=m+1;++j){
                map1[i][j]=';';
                vis[i][j]=false;
            }
        }
        for(register int i=1;i<=n;++i){
            for(register int j=1;j<=m;++j){
                std::cin>>map1[i][j];
            }
        }
        for(register int i=1;i<=n;++i){
            for(register int j=1;j<=m;++j){
                if(map1[i][j]=='*'){
                    if(map1[i][j+1]=='.'){
                        map1[i][j+1]='@';
                    }
                    if(map1[i+1][j]=='.'){
                        map1[i+1][j]='@';
                    }
                    if(map1[i][j-1]=='.'){
                        map1[i][j-1]='@';
                    }
                    if(map1[i-1][j]=='.'){
                        map1[i-1][j]='@';
                    }
                }
            }
        }//这段代码把黑棋的气全变成白棋
        register int cnt=0;
        for(register int i=1;i<=n;++i){
            for(register int j=1;j<=m;++j){
                if(cnt>=2){//如果白棋有大于等于一个地方是非法的,直接退出,黑棋是活的
                    j=m+1,i=n+1;
                    continue;
                }
                if(map1[i][j]=='@'){
                    if(!vis[i][j]&&!dfs(i,j)){
                        ++cnt;
                        // std::cout<<i<<" "<<j<<endl;
                    }
                }
            }
        }
        if(cnt>=2){
            puts("YES");
        }
        else{
            puts("NO");
        }
    }
    return 0;
}

/*
10
5 5
.****
*...*
*...*
*...*
*****
*/