样例输入:
5
样例输出:
0 0 1 2
分析:首先看数据范围我们可以发现a,b,c,d都是小于2500的,那么我们如果要是枚举的话最多只能枚举两个数,一种简单的思路是我们枚举前三个数,然后直接可以计算出第四个数是否存在,但看到数据范围后我们就可以发现这种方法是行不通的,那么接下来我们就需要想一种方法来简化。
想法就是使得在a和b足够小的前提下让c也尽可能地小,a和b足够小的前提应该怎么保证呢?这个很简单,我们可以直接暴力枚举,那么这也就意味着接下来我们要在O(1)的复杂度内找到满足n-a*a-b*b=c*c+d*d的最小c,容易发现我们可以预处理一个数组来实现这个功能,含义如下:
f[x]存储满足i*i+j*j=x的最小的i
其中初始化f数组为-1,那么当f[x]=-1时代表这样的数对是不存在的
那么在枚举a和b的前提下,我们只需要O(1)访问f[n-a*a-b*b]便可以知道是否存在满足n-a*a-b*b=c*c+d*d的数对(c,d),如果存在则可以直接找到c,所以整体的复杂度就可以满足题意了。
细节见代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
const int N=5e6+10;
int f[N];//f[x]存的是满足i*i+j*j=x的最小的i
int main()
{
memset(f,-1,sizeof f);
//f[x]=-1代表不存在数对(i,j)满足i*i+j*j=x
int n;
scanf("%d",&n);
for(int i=0;i*i*2<=n;i++)
for(int j=i;i*i+j*j<=n;j++)
if(f[i*i+j*j]==-1)
f[i*i+j*j]=i;
for(int i=0;i*i*4<=n;i++)
for(int j=0;i*i+j*j<=n/2;j++)
{
int t=n-i*i-j*j;
if(f[t]!=-1)
{
printf("%d %d %d %d",i,j,f[t],(int)sqrt(t-f[t]*f[t]));
return 0;
}
}
return 0;
}