[思维] P9417 [POI 2021_2022 R1] Druk

posted on 2024-05-21 05:42:00 | under | source

牛子题,刚开始还以为不可做,看了题解恍然大悟。那我也写一篇帮助后人理解。

首先枚举模板长度 \(len\),直觉告诉我们不用枚举太多无用长度。

结论 \(1\):不考虑字母,\(len\mid n\)\(len\mid m\) 是能够完全填充的充要条件。

证明:充分性易证,下面证必要性。考虑 \(len\nmid n\) 的情况,将列分为若干个大小为 \(len\) 的组,如 \([1,len]\)\([len+1,2len]\dots\)

每一列都要满足覆盖它的横着的模板串数量是 \(ilen+c\) 的,其中 \(c= n\bmod len\)

然后对于 \([1,len]\) 这一组,显然只能由 \(ilen+c\) 个起点在第一列的横着的模板串覆盖。再考虑 \([len+1,2len]\) 这一组,因为不能破坏前一组的覆盖方案,所以跨过 \([1,len]\)\([len+1,2len]\) 组的横着的模板串数量只能是 \(jlen\) 个。所以 \([len+1,2len]\) 组只能由 \(klen+c\) 的起点在第 \(len+1\) 的横着的模板串覆盖。

以此类推,就不可能出现 \(len\nmid m\) 的情况,即最后一组 \([plen+1,m]\) 的大小不为 \(len\)。否则,无法同时满足不破坏先前的覆盖方案且最后一组均被覆盖的要求。

(感觉不太严谨而且繁琐了些,应该有更好的证明)。

再考虑怎么判定。

结论 \(2\):从上到下,从左到右填充。若某一时刻即可以向下,又可以向右,则必定向右填。

证明:在前 \([1,p]\) 向下填充,\([p+1,len]\) 向右填充,接下来证 \(p=0\) 即可。

\([1,p]\) 部分:因为是向下填充,首字母一样,所以\([1,p]\) 全同。

\([p+1,len]\) 部分:后半部分可得 \([p+1,len]=[1,len-p]\),构成 \(\rm border\) 的形式,所以 \([1,p]\) 是模板串的周期。

综上所述,模板串全同。而我们是用模板串填充矩阵,所以矩阵全同,此时向右向下都无所谓了。

故一般情况下只能有 \(p=0\),即优先向右填充。

于是变成纯纯模拟题。

代码

#include<bits/stdc++.h>
using namespace std;

const int N = 1e3 + 5;
const int base = 1331, _base = 13331, mod = 998244353, _mod = 19260817;
int n, m, len, ans[N], cnt;
int X[N][N], Y[N][N], bas[N], K;
int _X[N][N], _Y[N][N], _bas[N], _K;
char c[N][N]; 
bool vis[N][N];

inline int H(int x, int y) {return (y + len - 1 > m) ? -1 : (1ll * (1ll * X[x][y + len - 1] - 1ll * X[x][y - 1] * bas[len] % mod + mod) % mod);}
inline int L(int x, int y) {return (x + len - 1 > n) ? -1 : (1ll * (1ll * Y[x + len - 1][y] - 1ll * Y[x - 1][y] * bas[len] % mod + mod) % mod);}
inline int _H(int x, int y) {return (y + len - 1 > m) ? -1 : (1ll * (1ll * _X[x][y + len - 1] - 1ll * _X[x][y - 1] * _bas[len] % _mod + _mod) % _mod);}
inline int _L(int x, int y) {return (x + len - 1 > n) ? -1 : (1ll * (1ll * _Y[x + len - 1][y] - 1ll * _Y[x - 1][y] * _bas[len] % _mod + _mod) % _mod);}
inline bool No_H(int x, int y) {if(y + len - 1 > m) return false; for(int i = y; i < y + len; ++i) if(vis[x][i]) return false; return true;}
inline bool No_L(int x, int y) {if(x + len - 1 > n) return false; for(int i = x; i < x + len; ++i) if(vis[i][y]) return false; return true;}
inline bool check(){
	memset(vis, 0, sizeof vis);
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j){
			if(vis[i][j]) continue;
			if(No_H(i, j) && H(i, j) == K && _H(i, j) == _K)
				for(int p = j; p < j + len; ++p) vis[i][p] = true;
			else if(No_L(i, j) && L(i, j) == K && _L(i, j) == _K)
				for(int p = i; p < i + len; ++p) vis[p][j] = true;
			else return false;
		}
	return true;
}
signed main(){
	bas[0] = _bas[0] = 1;
	for(int i = 1; i < N; ++i) bas[i] = 1ll * bas[i - 1] * base % mod, _bas[i] = 1ll * _bas[i - 1] * _base % _mod;
	cin >> n >> m; 
	for(int i = 1; i <= n; ++i) scanf("%s", c[i] + 1);
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j){
			X[i][j] = (1ll * X[i][j - 1] * base + c[i][j]) % mod, Y[i][j] = (1ll * Y[i - 1][j] * base + c[i][j]) % mod;
			_X[i][j] = (1ll * _X[i][j - 1] * _base + c[i][j]) % _mod, _Y[i][j] = (1ll * _Y[i - 1][j] * _base + c[i][j]) % _mod;
		}
	for(int i = 1; i <= max(n, m); ++i){
		if(n % i && m % i) continue;
		len = i, K = H(1, 1), _K = _H(1, 1);
		if(K != -1 && check()) ans[++cnt] = i;
		else{
			K = L(1, 1), _K = _L(1, 1);
			if(K != -1 && check()) ans[++cnt] = i;
		}
	}
	printf("%d\n", cnt);
	for(int i = 1; i <= cnt; ++i) printf("%d ", ans[i]);
	return 0;
}
posted @ 2026-01-13 11:18  Zwi  阅读(0)  评论(0)    收藏  举报