其他分享
首页 > 其他分享> > codeforces 1004 D. Sonya and Matrix 构造

codeforces 1004 D. Sonya and Matrix 构造

作者:互联网

原题地址:D. Sonya and Matrix

题目大意

称一个\(n*m\)的矩阵,里面恰好只有一个\(0\),且其他所有位置上的值恰好等于此位置到\(0\)点曼哈顿距离的矩阵为菱形矩阵.现在给出一个无序的长度为\(t\)的数组,构造一个菱形矩阵,所有元素恰好使用一次,或输出无解.

思路

这个构造一上手都没什么突破口,假如说从每个数出现次数上下手的话会发现很难确定位置,所以不妨就直接强加一些限制:首先记\(0\)点的位置是\((x,y)\)并且要求\(0\)点处于左上角,如果不在,那么可以把整个矩阵旋转使得他位于左上角.那么可以发现既然现在\(0\)点在左上角,那么最大距离的点应该是在右下角,显然右下角的值就应该是\(b=\max\{t\}\).那么可以得出一个结论:\(y = n + m - x - b\).式中\(b\)是已知量,剩下的\(n,m\)可以枚举\(t\)的约数,那么这样的话就可以表示出\(y\)了.也就是说可以先枚举所有可能的\(n\)和\(m\),在已知\(x\)的前提下就可以知道\(y\),之后整个矩阵的全貌也就可以直接构造出来了,剩下的问题就是\(x,y\)这两个值怎么确定其一.

假设矩阵的大小是毫无限制的,那么画图可以发现每个元素\(x\)出现的次数恰好是\(4*x\), 但是现实情况会有"切割"某些元素的情况发生,因为"切割"这件事情一定是横着竖着删无限制的矩阵的大小的,我们不妨关注一下左上角\(0\)的位置,那么从\(0\)这个点往上走或者往左走,走到恰好第一个被切割的剩下的位置的时候,这个距离正是\(0\)点的坐标,也就是说如果找到了哪个元素的个数被"切割掉了"那么这个元素恰好就是边界走到\(0\)的距离(事实上应该说是最小的被切割的元素),所以只需要枚举找出最小的那个元素\(x\)满足\(cnt[x] < 4 * x\),\(x\)就是\(0\)点的坐标,\(y\)即可表示出来了.

剩下的就是直接枚举\(n,m\)再直接暴力的求出每个点到\(0\)点的距离,记录每个距离对应的点的个数,之后直接看是否跟给定的数组\(t\)是一致的就可以了.注意在枚举\(n,m\)的时候,\(m\)可以直接表示成另外一个约数(约数是成对出现的),但是不能强加\(n\leq m\)的限制.

感觉这题时间复杂度上有点玄幻,搞不太清楚.

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)	

const int N = 1e6+7;
int a[N],cnt[N],r_cnt[N];

bool check()
{
	forn(i,1,N - 1)	if(r_cnt[i] != cnt[i])	return 0;
	return 1;
}

int main() 
{
	int t,maxv = 0;scanf("%d",&t);
	forn(i,1,t)	scanf("%d",&a[i]),++cnt[a[i]],maxv = max(maxv,a[i]);
	
	int x = 1;
	while(cnt[x] == 4 * x) ++ x;
	
	// cout << x << endl;
	
	forn(n,1,t)
	{
		if(t % n)	continue;
		int m = t / n;
		
		int y = n + m - maxv - x;
		if(x < 1 || x > n || y < 1 || y > m)	continue;
		
		memset(r_cnt,0,sizeof r_cnt);
		
		forn(i,1,n)	forn(j,1,m)	++r_cnt[abs(i - x) + abs(j - y)];
		
		if(check())	return printf("%d %d\n%d %d",n,m,x,y),0;
		
	}
	puts("-1");
	return 0;
}

标签:cnt,元素,矩阵,codeforces,恰好,枚举,Sonya,左上角,1004
来源: https://www.cnblogs.com/HotPants/p/14384382.html