qbxt 突破营 Day1 T4
考虑经典的俄罗斯方块游戏,二维平面上有若干个积木,他们会受重力的影响下落并堆叠。注意,积木只会竖直下落,如果下落过程中碰到了别的积木那么就会停下。例如下图:
不同颜色的块代表了不同的积木,这些积木下落之后会形如下图:
积木的形状可以任意的,可能跟传统的俄罗斯方块有一些不同,比如下图:
这张图中的积木下落后应该是这样的:
本题中,我们分别用
#
和.
表示有/无积木覆盖。为了方便起见,我们认为一个四联通块是一块积木。对于两个有积木覆盖的位置,如果他们共用一条边,则我们认为他们相邻,相邻的块构成的连通块被称为四联通块。在本题中,有一个给定的坐标
(第 行第 列,1下标),保证这个坐标一定被积木覆盖,你需要计算对于所有的 .
替换成#
的情况(注意,此时的四联通块可能会有变化),这个给定的坐标内的#
下落到哪个坐标。同时为了不让输出成为时间瓶颈,你只需要输出一个验证码即可。具体来说,如果平面内有
个 .
,他们的坐标分别是(满足从左上到右下,即 。将把仅 替换成 #
后下落到的坐标记为 ,则你需要输出 。( 即对 做除法之后的余数) 对于所有数据,
, , , 保证 的位置是'#'。
非常好的一道题
先考虑
考虑同一列上两个相邻的 #
,设他们所在连通块分别为 #
间隔距离
这是什么?差分约束!直接建图跑最短路,复杂度
显然可以优化,因为当在图中加入一个 #
无非就是加入至多
但我们要求的还是从超级源
复杂度优化成了
继续优化,我们要想尽办法把
这时候突然想起luogu上一个水帖:把边权为
我们也考虑这么拆点,因为
相邻的两个 #
之间连 .
和 .
之间连 #
在 .
上方时连
代码比较有纪念意义,所以在这里放一下
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?