qbxt 突破营 Day1 T4
考虑经典的俄罗斯方块游戏,二维平面上有若干个积木,他们会受重力的影响下落并堆叠。注意,积木只会竖直下落,如果下落过程中碰到了别的积木那么就会停下。例如下图:
不同颜色的块代表了不同的积木,这些积木下落之后会形如下图:
积木的形状可以任意的,可能跟传统的俄罗斯方块有一些不同,比如下图:
这张图中的积木下落后应该是这样的:
本题中,我们分别用
#
和.
表示有/无积木覆盖。为了方便起见,我们认为一个四联通块是一块积木。对于两个有积木覆盖的位置,如果他们共用一条边,则我们认为他们相邻,相邻的块构成的连通块被称为四联通块。在本题中,有一个给定的坐标 \((R,C)\)(第 \(R\) 行第 \(C\) 列,1下标),保证这个坐标一定被积木覆盖,你需要计算对于所有的
.
替换成#
的情况(注意,此时的四联通块可能会有变化),这个给定的坐标内的#
下落到哪个坐标。同时为了不让输出成为时间瓶颈,你只需要输出一个验证码即可。具体来说,如果平面内有 \(k\) 个
.
,他们的坐标分别是 \((r_1,c_1),(r_2,c_2),...,(r_k,c_k)\)(满足从左上到右下,即\((\forall i)(\forall j)(i\in \mathbb{N} \bigwedge j\in \mathbb{N}\land 1\leq i<j\leq k \rightarrow r_i<r_j \bigvee(r_i=r_j\land c_i<c_j))\)。将把仅 \((r_i,c_i)\) 替换成#
后 \((R,C)\) 下落到的坐标记为 \((R_i,C_i)\),则你需要输出 \((\sum_{i=1}^{k} R_i\times 2021^{k- i}) \bmod 998244353\)。(\(\bmod 998244353\) 即对 \(998244353\) 做除法之后的余数)对于所有数据,\(1\leq n\times m\leq 10^7\), \(1\leq R\leq n\), \(1\leq C\leq m\), 保证\((R,C)\)的位置是'#'。
非常好的一道题
先考虑 \(nm \leq 2000\) 的部分,考虑模拟下落过程,设 \(d_i\) 表示第 \(i\) 块下落距离。
考虑同一列上两个相邻的 #
,设他们所在连通块分别为 \(u,v\) ,则显然我们要求 \(d_u \leq d_v + l - 1\) ,其中 \(l\) 两个 #
间隔距离
这是什么?差分约束!直接建图跑最短路,复杂度 \(O((nm)^2 \log nm)\)
显然可以优化,因为当在图中加入一个 #
无非就是加入至多 \(4\) 条边和一个点
但我们要求的还是从超级源 \(S\rightarrow (R,C)\) 的最短路。我们为什么不从 \(S\) 跑一边最短路, \((R,C)\) 跑一边最短路,然后合并呢?
复杂度优化成了 \(O(nm \log nm)\)
继续优化,我们要想尽办法把 \(\log\) 去掉。发现复杂度瓶颈在 dijkstra
这时候突然想起luogu上一个水帖:把边权为 \(w\) 的边拆成 \(w\) 个为 \(1\) 的边跑 bfs 复杂度就变成 \(O(n)\) 了
我们也考虑这么拆点,因为 \(l \leq \max(m,n)\) ,而且这个图我们显然可以建成一个网格图,复杂度是不变的。于是我们有以下建图思路:
相邻的两个 #
之间连 \(0\) 边,.
和 .
之间连 \(1\) ,#
在 .
上方时连 \(1\) ,下方连 \(0\) 。最终跑 01bfs 。复杂度 \(O(nm)\)
代码比较有纪念意义,所以在这里放一下
#include <bits/stdc++.h>
// #pragma GCC optimize(2)
#define pcn putchar('\n')
#define ll long long
#define LL __int128
#define pii pair<int, int>
#define pli pair<ll, int>
#define pil pair<int, ll>
#define pll pair<ll, ll>
#define MP make_pair
#define fi first
#define se second
#define gsize(x) ((int)(x).size())
#define Min(a, b) (a = min(a, b))
#define Max(a, b) (a = max(a, b))
#define For(i, j, k) for(int i = (j), END##i = (k); i <= END##i; ++ i)
#define For__(i, j, k) for(int i = (j), END##i = (k); i >= END##i; -- i)
#define Fore(i, j, k) for(int i = (j); i; i = (k))
//#define random(l, r) ((ll)(rnd() % (r - l + 1)) + l)
using namespace std;
namespace IO {
template <typename T> T read(T &num){
num = 0; T f = 1; char c = ' '; while(c < 48 || c > 57) if((c = getchar()) == '-') f = -1;
while(c >= 48 && c <= 57) num = (num << 1) + (num << 3) + (c ^ 48), c = getchar();
return num *= f;
}
ll read(){
ll num = 0, f = 1; char c = ' '; while(c < 48 || c > 57) if((c = getchar()) == '-') f = -1;
while(c >= 48 && c <= 57) num = (num << 1) + (num << 3) + (c ^ 48), c = getchar();
return num * f;
}
template <typename T> void Write(T x){
if(x < 0) putchar('-'), x = -x;
if(x == 0){putchar('0'); return ;}
if(x > 9) Write(x / 10);
putchar('0' + x % 10); return ;
}
void putc(string s){ int len = s.size() - 1; For(i, 0, len) putchar(s[ i ]); }
template <typename T> void write(T x, string s = "\0"){ Write( x ), putc( s ); }
}
using namespace IO;
//mt19937_64 rnd(time(0));
//#define random(l, r) ((ll)(rnd() % (r - l + 1)) + l)
#ifdef LOCAL
template <typename T> void debug(T x, string s = "\0"){ write(x, s); }
#else
template <typename T> void debug(T x, string s = "\0"){}
#endif
/* ====================================== */
const int maxn = 1e7 + 50;
const int maxd = 4;
const int INF = 1e9 + 50;
const ll mod = 998244353;
const int tx[ 5 ] = {0, 0, 0, -1, 1};
const int ty[ 5 ] = {0, 1, -1, 0, 0};
int n, m, R, C;
char ct[ maxn ];
vector<vector<int> > a;
pii e[ maxn ][ maxd ]; int dout[ maxn ];
queue<int> q[ 2 ];
int dis[ 2 ][ maxn ];
void rsz(vector<vector<int> > &a){ a.clear(); a.resize(n + 5); For(i, 0, n + 4) a[ i ].resize(m + 5); }
void addedge(int u, int v, int w, bool dir = 0){ if(dir) swap(u, v); e[ u ][ dout[ u ] ++ ] = MP(v, w); }
int GetPos(int x, int y){ return (x - 1) * m + y; }
void ClearEdge(){ For(i, 1, n * m) dout[ i ] = 0; }
void BuildEdge(bool dir){
for(int x = 1, e = 1; x <= n; ++ x)
for(int y = 1; y <= m; ++ y, ++ e){
if(a[ x ][ y ]){
if(y > 1 && a[ x ][ y - 1 ]) addedge(e, e - 1, 0, dir); if(y < m && a[ x ][ y + 1 ]) addedge(e, e + 1, 0, dir);
if(x > 1) addedge(e, e - m, 0, dir); if(x < n && a[ x + 1 ][ y ]) addedge(e, e + m, 0, dir);
}
else if(x > 1) addedge(e, e - m, 1, dir);
}
}
void bfs(int *dis){
while(!q[ 0 ].empty() || !q[ 1 ].empty()){ int u; For(o, 0, 1) if(!q[ o ].empty()){ u = q[ o ].front(), q[ o ].pop(); break; }
For(i, 0, dout[ u ] - 1){ int v = e[ u ][ i ].fi, w = e[ u ][ i ].se; if(dis[ v ] > dis[ u ] + w) dis[ v ] = dis[ u ] + w, q[ w ].push(v); }
}
}
void mian(){
read(n), read(m), read(R), read(C); rsz(a);
For(i, 1, n){ scanf("%s", ct + 1); For(j, 1, m) a[ i ][ j ] = (ct[ j ] == '#'); }
BuildEdge(0);
For(o, 0, 1) while(!q[ o ].empty()) q[ o ].pop(); For(i, 1, n * m) dis[ 0 ][ i ] = INF;
For(e, (n - 1) * m + 1, n * m) q[ 0 ].push(e), dis[ 0 ][ e ] = 0; bfs(dis[ 0 ]);
// For(i, 1, n * m) write(dis[ 0 ][ i ], " "); pcn; // debug;
ClearEdge(), BuildEdge(1);
For(o, 0, 1) while(!q[ o ].empty()) q[ o ].pop(); For(i, 1, n * m) dis[ 1 ][ i ] = INF; q[ 0 ].push(GetPos(R, C)), dis[ 1 ][ GetPos(R, C) ] = 0; bfs(dis[ 1 ]);
// For(i, 1, n * m) write(dis[ 1 ][ i ] >= INF ? -1 : dis[ 1 ][ i ], " "); pcn; // debug;
ll ans = 0;
for(int x = 1, e = 1; x <= n; ++ x)
for(int y = 1; y <= m; ++ y, ++ e){
if(a[ x ][ y ]) continue; pii ein[ maxd ], eout[ maxd ]; int ci = 0, co = 0;
if(y > 1 && a[ x ][ y - 1 ]) ein[ ci ++ ] = MP(e - 1, 0), eout[ co ++ ] = MP(e - 1, 0);
if(y < m && a[ x ][ y + 1 ]) ein[ ci ++ ] = MP(e + 1, 0), eout[ co ++ ] = MP(e + 1, 0);
if(x > 1){
eout[ co ++ ] = MP(e - m, 0);
if(a[ x - 1 ][ y ]) ein[ ci ++ ] = MP(e - m, 0);
}
if(x < n){
ein[ ci ++ ] = MP(e + m, !a[ x + 1 ][ y ]);
if(a[ x + 1 ][ y ]) eout[ co ++ ] = MP(e + m, 0);
}
int res = dis[ 0 ][ GetPos(R, C) ];
if(x == n) For(i, 0, co - 1) Min(res, dis[ 1 ][ eout[ i ].fi ] + eout[ i ].se);
For(i, 0, ci - 1) For(j, 0, co - 1) Min(res, dis[ 0 ][ ein[ i ].fi ] + dis[ 1 ][ eout[ j ].fi ] + ein[ i ].se + eout[ j ].se);
ans = (ans * 2021 + R + res) % mod;
// printf("%d %d %d\n", x, y, res); // debug;
}
write(ans, "\n");
}
void init(){
}
void treatment(){
}
int main() {
#ifdef LOCAL
freopen("data.in", "r", stdin);
// freopen("tetris.out", "w", stdout);
#else
freopen("tetris.in", "r", stdin);
freopen("tetris.out", "w", stdout);
#endif
treatment();
int T = 1;
// read(T);
while(T --){
init();
mian();
}
return 0;
}