偶尔写写题,锻炼一下脑子

ABC460d

  很好的一个题,使我大脑黑白颠倒。感觉这个题,考察了比较完整的思考模式。

  神秘洛谷不接受我的题解,简直妙妙。

  感觉\(F<D\)

  首先,看到\(10^{100}\)次方,应该想到的是在某一个操作下事物的状态不变,或者进入到了某种妙妙循环,是算法直觉?手玩样例发现,是后者,并且循环节为\(2\),进入循环以后,黑白方块的颜色应该是颠倒的。于是,应该思考从什么时候开始进入循环。继续手玩样例后,发现进入循环的时间不一样。

  在思考无果后,应该想到,将整个方块进入循环的时间划分成小格子进入循环的时间,也就是大问题无法解决的时候将大问题划分成若干个子问题,属于算法思想?格子只有黑或者白,由于白色只能被黑色"污染",所以思考白色格子什么时候进入循环。不难想到,当一个白色格子被染成黑色后,下一次操作中,会将周围的白色格子染黑,并且自身变白;下下一次操作中,其相邻的格子会将周围的白色格子染黑,包括白色格子自己,此后不断循环,因为只能黑白交替,所以循环节为\(2\)。所以,白色格子进入循环的时间就是第一次被染成黑色的时间,白色格子只能被相邻的黑色格子污染,或者相邻的白色格子污染后再污染它,其相邻格子也是同理。于是我们发现,黑色格子可以作为污染源,不断传递颜色,设\(dpij\)表示格子被污染的时间,所有的污染源设为\(0\)(坑点\(1\)),则有\(dpij = dpab + 1\)(\(a, b\)是相邻格子坐标)。开始思考黑色格子什么时候进入循环,如果黑色格子周围存在白色格子,一开始便进入了循环,如果周围没有,需要等待周围的黑色格子变白,然后这些白色格子被污染后才能改变状态,在某一段持续的时间内都是白色。这说明,并不是所有黑色格子都是污染源,只有周围存在白色格子的黑色格子才是污染源,可以通过这个白色格子将颜色传递;反之只能在变成白色格子后等待被污染(没有考虑这个会\(wa\)一半)。同时证明,我们的\(dp\)方程不是针对白色格子,而是针对所有不能做污染源的格子。

  思考怎么更新\(dp\)状态,每个格子都会受到周围一圈格子的影响,所以,状态更新应该从周围一圈格子传递过来,周围一圈格子又从它们周围一圈格子传递状态,最终追溯到污染源,于是想到多源\(bfs\),将所有污染源加入队列不断更新状态,这是算法应用。得到格子进入循环时间,当最晚的格子进入循环,整个方块进入循环。由于颜色随时间不断交替,所以可以考虑对时间分奇偶,假设格子在时间\(t\)进入循环,\(t\)是偶数,则最终颜色和当前一样(\(10^{100}\)是偶数),反之最终颜色和当前相反。

  发现样例\(2\)无法通过,直觉告诉我们样例\(2\)是种特例(坑点\(2\)),手玩样例后发现,当方块初始状态下同色时,最终是白色,原因是缺少污染源。最终得到\(AC\)代码

复制代码
#define rep(i, x, y) for (int i = x; i <= y; i++)
#define per(i, x, y) for (int i = x; i >= y; i--)
#define t2 tuple<int, int>
const int inf = 1e6 + 10;
int dx[] = {-1, -1, -1, 0, 1, 1, 1, 0};
int dy[] = {-1, 0, 1, 1, 1, 0, -1, -1};
void solve() {
    int n, m;
    cin >> n >> m;
    vector vec(n + 1, vector<char>(m + 1));
    vector dp(n + 1, vector<int>(m + 1, inf));
    queue<t2> q;
    rep(i, 1, n) rep(j, 1, m) cin >> vec[i][j];
    rep(i, 1, n) rep(j, 1, m) {
        if (vec[i][j] == '.') continue;
        rep(k, 0, 7) {
            int a = i + dx[k], b = j + dy[k];
            if (a < 1 || a > n || b < 1 || b > m) continue;
            if (vec[a][b] == '.') {
                q.push({i, j});
                dp[i][j] = 0;
                break;
            }
        }
    }
    while (q.size()) {
        auto [x, y] = q.front();
        q.pop();
        rep(i, 0, 7) {
            int a = x + dx[i], b = y + dy[i];
            if (a < 1 || a > n || b < 1 || b > m) continue;
            if (dp[a][b] > dp[x][y] + 1) {
                dp[a][b] = dp[x][y] + 1;
                q.push({a, b});
            }
        }
    }
    rep(i, 1, n) {
        rep(j, 1, m) {
            if (dp[i][j] & 1 || dp[i][j] == inf) cout << '.';
            else cout << '#';
        }
        cout << '\n';
    }
}