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)可以直接表示成另外一个约数(约数是成对出现的),但是不能强加(nleq 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
%d %d",n,m,x,y),0;
		
	}
	puts("-1");
	return 0;
}
原文地址:https://www.cnblogs.com/HotPants/p/14384382.html