题意
给出一个n*m的01矩阵a,求满足下列条件的k:
- k>1
- 网格为点,每个网格和它四个方向相邻的网格都存在一条边,从某个 a i j = 1 a_{ij} = 1 aij=1的网格出发,每次只能向上下左右任意一方向移动k个位置,所经过的网格必须都为1,存在某个移动方案,使得边不能重复经过,覆盖矩阵中所有的1。
思路
-
首先图必须要连通
-
如果我们确定了一些点,称为拐弯点,在这些点移动出现方向的变换,那么建立一个新图,顶点为这些拐弯点,将位于同一行或者同一列,相邻的两个被1所连接的拐弯点连边,边长就是他们的距离差。如果新图存在欧拉路径或者欧拉回路,那么方案一定存在,那么最大的答案就是边长的gcd。
-
分析发现如果一个点满足向上下存在至少一个相邻的1 并且左右存在一个相邻的1,它必定为拐弯点
如
1 0
1 10 1 0
1 1 10 1 0
1 1 1
0 1 0
此外,存在方案的图必定不存在任意如下子矩阵
1 1
1 1
原因:
设如果该图存在一个方案,但是它在某个拐弯点不拐弯,继续保持原来方向前进,但是由于该点的度大于2,如果保持方向,必然将从另外一个方向进入该点,形成一个回路,那么将回路倒着跑一遍也是合法的(从改变方向的那个边出去,原方向的边进入)。所以如果一个点满足向上下存在至少一个相邻的1 并且左右存在一个相邻的1,它必定为拐弯点
所以做法只要确定拐弯点,求出所有边长的gcd,然后答案激素gcd的因子扣掉1的情况。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,int>pii;
typedef vector<int>vi;
#define rep(i,a,b) for(int i=(a);i<(b);i++)
#define fi first
#define se second
#define de(x) cout<<#x<<"="<<x<<"\n"
#define dd(x) cout<<#x<<"="<<x<<" "
#define pb(x) push_back(x)
#define all(x) x.begin(),x.end()
#define sz(x) (int)x.size()
#define lowbit(a) ((a)&-(a))
#define per(i,a,b) for(int i=(b)-1;i>=(a);--i)
const int N=3e3+5;
vi ans;
int n ,m;
int a[1004][1005],in[N][N];
bool vis[N*N],use[N][N],lab[N*N];
int d[5][2] ={{1,0},{0,1}};
vector<pii> g[N*N]; //pii<点,边编号>
int p[N];
int num;
void dfs(int u){
num++;
vis[u] = true;
for(auto it:g[u]){
if(vis[it.fi])continue;
dfs(it.fi);
}
}
inline to(int x,int y){return (x - 1)*m + y;}
int rt = -1;
int l[N][N],h[N][N];
void init(){
rep(i , 1 , n + 1)
rep( j , 1 , m + 1){
if(!a[i][j])continue;
l[i][j] = l[i][j - 1] + 1;
h[i][j] = h[i-1][j] + 1;
}
}
int mx = 0;
void solve(int x){
if(x == 0)return;
if(mx == 0 )mx = x;
else mx = __gcd(mx, x);
}
int main()
{
cin>>n>>m;
rep(i , 1 , n + 1)
rep(j , 1 , m + 1)cin>>a[i][j];
int tot = 0,ss = 0,odd = 0;
init();
rep(i , 1 , n + 1)
rep(j , 1 , m + 1){
if(!a[i][j])continue;
if(rt == -1)rt = to(i,j);
rep( k , 0 ,2){
int nx = i + d[k][0],ny = j + d[k][1];
if(!a[nx][ny])continue;
g[to(i,j)].pb(pii(to(nx,ny),++tot));
g[to(nx,ny)].pb(pii(to(i,j),tot));
}
ss++;
if((a[i + 1][j] || a[i - 1][j]) && (a[i][j + 1]||a[i][j-1]))use[i][j] = true;
int now = a[i + 1][j] + a[i - 1][j] + a[i][j + 1] + a[i][j-1];
if(now&1)odd++,use[i][j] = true;
}
dfs(rt);
if(num != ss || odd>2){
cout<<-1;
return 0;
}
rep(i , 1 , n + 1)
rep( j , 1 , m + 1){
if(!use[i][j])continue;
solve(l[i][j] - 1);
solve(h[i][j] - 1);
}
if(mx <= 1)cout<<-1;
else{
rep(i , 2 , mx)
if(mx%i == 0)cout<<i<<" ";
cout<<mx<<"\n";
}
return 0;
}