P2232 [HNOI2002] 填数游戏 题解
一道简单却又不简单的简单题,为啥没有题解啊
题目大意:试找出一个
考虑解构题目,为以下要求,逐个攻破:
-
-
填入表格的数不能相等。
-
每行每列的和小于某上限。
无解情况和基础构造
对任意正整数
证明:注意到,如果存在两数列满足其平方和为一完全平方数,即:
那么,以下构造为一个满足第一条件的
第
问题就变成找长度为
根据小学二年级的恒等式有
通项表达式是这个,请自行验证:
受到启发,我们可以用任意勾股数
剩下的,就是说明,我们可以找到两个这样的数列,构造出上述矩阵,没有重复的平方数。除非数列中有两个数相同,否则重复的平方数不可能在同行或同列。两数平方相同,当且仅当,两者相差的质因子以各种方式被拉平,这是很好规避的,只要一方引入了特有的质因子,任意两数就会因为这个质因子的次数不同而不会相等。
举个例子,
长整形限制和平方差
如果没有
引用同样的记号,我们有:
不妨令
注意这里除最后一项前面全部自由,就是说你可以随便选择前面的数字,然后找到
这里数字可以变得很小,上面
剩下的就是如何保证没有平方重复,方法就太多了。比如让行数列和列数列不共享任何质因子,或者直接质因子分解,或者搜索,或者奇奇怪怪的构造,等等。这是你的自由,请自己做主,这里不再赘述。注意,一定要判断最后一项是否也满足条件。
代码与验证程序
因为 SPJ 不完善,所以自己写了个验证程序。(抱歉,暂时不会写 SPJ,大佬可以随意更改)
\\verifier_2232.cpp
#include<iostream>
#include<fstream>
#include<cmath>
typedef long long ll;
const long long lim=1e17;// upper bound
bool issq(ll a){ll b=sqrt(a);return b*b==a;}
int main(int argc,char*argv[]){
std::fstream inf("testdata.in");// <----- your answer from, change inf to cin if std::cin.
std::fstream ouf("testdata.out");// <----- the judge result to, change ouf to cout if std::cout.
int m,n;ll a[300];inf>>n>>m;// <----- requires n, m input, modify if needed.
for(int i=0;i<n;i++)for(int j=0;j<m;j++){
inf>>a[i*m+j];if(!issq(a[i*m+j])){
ouf<<"Wrong answer! Detected non-square at ("<<i+1<<","<<j+1<<")!\n";return 0;}
}
for(int i=0;i<n*m;i++)for(int j=i+1;j<n*m;j++)if(a[i]==a[j]){
ouf<<"Wrong answer! Detected repeated value: "<<a[i]<<"!\n";return 0;
}
for(int i=0;i<n;i++){ll lsum=0;
for(int j=0;j<m;j++)lsum+=a[i*m+j];if(!issq(lsum)||lsum>=lim){
ouf<<"Wrong answer! Row "<<i+1<<" sums to non-square or is too large!\n";return 0;}
}
for(int j=0;j<m;j++){ll csum=0;
for(int i=0;i<n;i++)csum+=a[i*m+j];if(!issq(csum)||csum>=lim){
ouf<<"Wrong answer! Column "<<j+1<<" sums to non-square or is too large!\n";return 0;}
}
ouf<<"Answer correct!\n";
}
代码正文
#include<cstdio>//P2232 [HNOI2002] 填数游戏
#define sqr(x) ((x)*(x))
const int N=22+32;
long long r0[N],r1[N];
int primes[N]={3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,
59,61,67,71,73,79,83,89,97,101,103,107,109,113,127};
void manage(int n,int m){
for(int i=0;i<n;++i)r0[i]=primes[i];if(n%2==0)r0[0]<<=1;
for(int i=0;i<m;++i)r1[i]=primes[i+n];if(m%2==0)r1[0]<<=1;
}
void generate(long long* rw,int n,long long tmp=0){
for(int i=0;i<n;++i)tmp+=sqr(rw[i]);rw[n]=(tmp-1)/2;
for(int j=3;j<100;j+=2)if((tmp/j>rw[n-1]*2+j)&&(tmp-j*j)%(2*j)==0)rw[n]=(tmp-j*j)/2/j;
}
int main(){
// freopen("testdata.in","w",stdout);printf("%d %d\n",n,m);
int n,m;scanf("%d%d",&n,&m);
// int swap=0;if(n<m)n^=m^=n^=m,swap=1;if(n==11&&m==6)swap^=1,n^=m^=n^=m;
manage(n-1,m-1);generate(r0,n-1);generate(r1,m-1);//if(swap)n^=m^=n^=m;
for(int i=0;i<n;++i)for(int j=0;j<m;++j)
{printf("%lld",swap?sqr(r1[i]*r0[j]):sqr(r0[i]*r1[j]));printf((j+1==m)?"\n":" ");}
}