P1586 题解

· · 个人记录

暴力

由于 n 很小,所以可以想到用 O(n^2) 的时间枚举四个数,卡卡常,或者开 O_2,可以水过去

于是大致有这样一份代码

    while(T--){
        scanf("%d",&n);
        cnt=0;
        e=sqrt(n);
        for(int i1=0;i1<=e;i1++)
            for(int i2=i1;i2<=e;i2++)
                for(int i3=i2;i3<=e;i3++)
                    for(int i4=i3;i4<=e;i4++)
                        if(i1*i1+i2*i2+i3*i3+i4*i4==n)
                            cnt++;
        printf("%d\n",cnt);
    }

优化

题目要求 n=i_1^2+i_2^2+i_3^2+i_4^2(n,i1,i2,i3,i4\in N)

为了避免重复我们规定 0\le i_1\le i_2 \le i_3 \le i_4 \le \sqrt n

如果已知 i_1,i_2,i_3,那么再去枚举 i_4 是否会显得很 silly ?

我们可以判断 i_4=\sqrt{n-i_1^2-i_2^2-i_3^2} 是否合法

可以打表一下,直接判断,于是省去了一重循环 三重循环,复杂度为 $O(Tn\sqrt n)$,是对的了,加上一些剪枝,卡卡常,快得飞起 代码 ```cpp 代码中 f[i]=i^2,减少计算量, ps[n]=sqrt(n),当然仅限 n 是完全平方数,因为不是整数的话没有意义 #include<cstdio> int f[200]; int ps[40010]; int main(){ int T,n,cnt; for(int i=0;i<=181;i++){ f[i]=i*i; ps[f[i]]=i; } scanf("%d",&T); while(T--){ scanf("%d",&n); cnt=0; for(register int i1=0;i1<=181;i1++){ if(f[i1]>n) break; for(register int i2=i1;i2<=181;i2++){ if(f[i1]+f[i2]>n) break; for(register int i3=i2;i3<=181;i3++){ int t=n-f[i1]-f[i2]-f[i3]; if(t<0) break; if(ps[t]>=i3&&ps[t]) cnt++; } } } printf("%d\n",cnt); } return 0; } ``` 提交记录(O2与无O2) ![](https://cdn.luogu.com.cn/upload/image_hosting/v7m2qbrv.png)