qbxt 突破营 Day1 T4

考虑经典的俄罗斯方块游戏,二维平面上有若干个积木,他们会受重力的影响下落并堆叠。注意,积木只会竖直下落,如果下落过程中碰到了别的积木那么就会停下。例如下图:

01.png

不同颜色的块代表了不同的积木,这些积木下落之后会形如下图:

02.png

积木的形状可以任意的,可能跟传统的俄罗斯方块有一些不同,比如下图:

03.png

这张图中的积木下落后应该是这样的:

04.png

本题中,我们分别用#.表示有/无积木覆盖。为了方便起见,我们认为一个四联通块是一块积木。对于两个有积木覆盖的位置,如果他们共用一条边,则我们认为他们相邻,相邻的块构成的连通块被称为四联通块。

在本题中,有一个给定的坐标 (R,C)(第 R 行第 C 列,1下标),保证这个坐标一定被积木覆盖,你需要计算对于所有的.替换成#的情况(注意,此时的四联通块可能会有变化),这个给定的坐标内的#下落到哪个坐标。同时为了不让输出成为时间瓶颈,你只需要输出一个验证码即可。

具体来说,如果平面内有 k.,他们的坐标分别是 (r1,c1),(r2,c2),...,(rk,ck)(满足从左上到右下,即(i)(j)(iNjN1i<jkri<rj(ri=rjci<cj))。将把仅 (ri,ci) 替换成#(R,C) 下落到的坐标记为 (Ri,Ci),则你需要输出 (i=1kRi×2021ki)mod998244353。(mod998244353 即对 998244353 做除法之后的余数)

对于所有数据,1n×m107, 1Rn, 1Cm, 保证(R,C)的位置是'#'。

非常好的一道题

先考虑 nm2000 的部分,考虑模拟下落过程,设 di 表示第 i 块下落距离。

考虑同一列上两个相邻的 # ,设他们所在连通块分别为 u,v ,则显然我们要求 dudv+l1 ,其中 l 两个 # 间隔距离

这是什么?差分约束!直接建图跑最短路,复杂度 O((nm)2lognm)

显然可以优化,因为当在图中加入一个 # 无非就是加入至多 4 条边和一个点

但我们要求的还是从超级源 S(R,C) 的最短路。我们为什么不从 S 跑一边最短路, (R,C) 跑一边最短路,然后合并呢?

复杂度优化成了 O(nmlognm)

继续优化,我们要想尽办法把 log 去掉。发现复杂度瓶颈在 dijkstra

这时候突然想起luogu上一个水帖:把边权为 w 的边拆成 w 个为 1 的边跑 bfs 复杂度就变成 O(n)

我们也考虑这么拆点,因为 lmax(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;
}

posted @   FOX_konata  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
点击右上角即可分享
微信分享提示