[思维] 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;
}

浙公网安备 33010602011771号