CF1004D Sonya and Matrix - 构造
题解
设矩阵是 \(n\) 行 \(m\) 列的,且唯一的值为 \(0\) 的点在 \((x,y)\)。不妨设 \(x\le n-x+1,y\le m-y+1\),因为可以通过旋转和翻转矩阵将 \((x,y)\) 放到这个区域内。
于是,矩阵中离 \((x,y)\) 最远的点一定是 \((n,m)\)。它们之间的距离是 \(\lvert n-x\rvert+\lvert m-y\rvert=n+m-x-y\)。找到给定的 \(t\) 个数中的 \(\max\),设为 \(\alpha\),则有 \(\alpha=n+m-x-y\)。
可以发现,矩阵中值为 \(a\) 的数(也即和 \((x,y)\) 的距离为 \(a\) 的位置)在不卡边界的情况下有 \(4a\) 个。
如下图:
图中 \(1,2\) 各有 \(4,8\) 个,而从 \(3\) 开始就卡了边界,因此 \(3\) 只有 \(8\) 个而不是 \(12\) 个。
设最小的被卡边界的数是 \(v\),易知 \(\min(x,y)=v\)。不妨设 \(x=v\),根据上面讨论过的 \(\alpha=n+m-x-y\),可以算出 \(y\)。
此时,枚举 \(n\times m\) 个位置,并检查其是否与给定的 \(t\) 个数 \(a_1,a_2,\dots,a_t\) 相同即可。
由于需要满足 \(n\times m=t\),所以枚举 \(t\) 的约数作为 \(n,m\) 的值即可。
时间复杂度 \(\mathcal{O}(t\operatorname{d}(t))\),\(\operatorname{d}\) 是约数个数函数。
代码
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define For(Ti,Ta,Tb) for(int Ti=(Ta);Ti<=(Tb);++Ti)
#define Dec(Ti,Ta,Tb) for(int Ti=(Ta);Ti>=(Tb);--Ti)
template<typename T> void Read(T &_x){
_x=0;int _f=1;
char ch=getchar();
while(!isdigit(ch)) _f=(ch=='-'?-1:_f),ch=getchar();
while(isdigit(ch)) _x=_x*10+(ch^48),ch=getchar();
_x*=_f;
}
template<typename T,typename... Args> void Read(T &_x,Args& ...others){
Read(_x);Read(others...);
}
typedef long long ll;
const int T=1e6+5;
int t,cnt[T],mx,cnt_[T];
int Abs(int x){
return x<0?-x:x;
}
int main(){
Read(t);
For(i,1,t){
int x;Read(x);
++cnt[x],mx=max(mx,x);
}
int x=0;
For(i,1,t){
if(cnt[i]!=4*i){
x=i;break;
}
}
For(n,1,t){
if(t%n) continue;
int m=t/n,y=n+m-x-mx;
if(x>n||y<=0) continue;
memset(cnt_,0,sizeof cnt);
For(i,1,n)
For(j,1,m) ++cnt_[Abs(i-x)+Abs(j-y)];
if(memcmp(cnt,cnt_,sizeof cnt)==0){
printf("%d %d\n%d %d\n",n,m,x,y);
return 0;
}
}
puts("-1");
return 0;
}
Written by Alan_Zhao