首页 > 其他分享 >AcWing 1221 四平方和

AcWing 1221 四平方和

时间:2022-10-24 10:00:39浏览次数:80  
标签:std 10 1221 int 平方和 using include AcWing

\(AcWing\) \(1221\). 四平方和 + 自定义排序(重载<)+二分

题目传送门

一、题目大意

四平方和定理,又称为 拉格朗日定理

每个正整数都可以表示为至多 \(4\) 个正整数的平方和。

如果把 \(0\) 包括进去,就正好可以表示为 \(4\) 个数的平方和。

比如:
\(5=0^2+0^2+1^2+2^2\)
\(7=1^2+1^2+1^2+2^2\)

对于一个给定的正整数,可能存在多种平方和的表示法。

要求你对 \(4\) 个数排序:

\(0 \leqslant a \leqslant b \leqslant c \leqslant d\)

并对所有的可能表示法按 \(a,b,c,d\) 为联合主键升序排列,最后输出第一个表示法。

输入格式
输入一个正整数 \(N\)。

输出格式
输出\(4\)个非负整数,按 从小到大 排序,中间用空格分开。

数据范围
\(0<N<5∗10^6\)

二、暴力解法

#include <bits/stdc++.h>
using namespace std;

//通过了 10/11个数据
int main() {
    int n;
    cin >> n;
    for (int a = 0; a * a <= n; a++)
        for (int b = a; a * a + b * b <= n; b++)
            for (int c = b; a * a + b * b + c * c <= n; c++) {
                int t = n - a * a - b * b - c * c;
                int d = sqrt(t);
                if (d * d == t) {
                    printf("%d %d %d %d\n", a, b, c, d);
                    return 0;
                }
            }
    return 0;
}

暴力 \(O(N^3)\)
一般对于\(C++\)里面的代码,\(1s\)也就运行\(10^8\),这个题的最大取到\(10^6\),开方\(10\)的\(3\)次方,\(N\)的\(3\)次方,大概\(10^9\)的运算次数,肯定会超时。

三、二分作法

枚举\(c\)和\(d\),将\(c^2+d^2\)存至数组中,再枚举\(a\)和\(b\),查找\(n−a^2−b^2\)是否在数组中出现过。时间复杂度:\(O(n^2logn^2)\)。

#include <bits/stdc++.h>
using namespace std;

const int N = 5e6 + 10;
struct Node {
    int c, d, sum;
    bool operator<(const Node &t) const {
        //对所有的可能表示法按 a,b,c,d 为联合主键升序排列,最后输出第一个表示法。
        //因为这个第一个表示法,使得我们首先需要对sum进行升序排列,如果sum值一样,就需要对c进行升序排列.至于说d,其实无所谓了,因为本题中不可能存在
        // sum一样,c也一样的场景,但也许有的题不行,需要写全排序办法
        if (sum != t.sum) return sum < t.sum;
        if (c != t.c) return c < t.c;
        return d < t.d;
    }
} f[N];

int n;
int idx;

int main() {
    cin >> n;

    //枚举c^2+d^2
    for (int c = 0; c * c <= n; c++)
        for (int d = c; c * c + d * d <= n; d++)
            f[idx++] = {c, d, c * c + d * d};

    //结构体排序
    sort(f, f + idx);

    //枚举a^2+b^2
    for (int a = 0; a * a <= n; a++) {
        for (int b = a; a * a + b * b <= n; b++) {
            int t = n - a * a - b * b;
            int p = lower_bound(f, f + idx, Node{0, 0, t}) - f;

            /*
            结构体+lower_bound需要注意的事项:
            一、结构体中二分查找,需要封装成无用项置0的结构体:
              (1) 把我们需要查找的数封装成一个结构体。然后才可以在结构体重进行查找。即使我们只需要针对某一维进行查找,也需要把整个结构体构造出来。
              (2) 这里我只需要查找第一维,并且我对第一维进行了排序,只有有序数列才可以进行二分,然后在查找的时候,把其他维置零即可。但是必须要封装成一个结构体

            二、最终的结果需要进行判断
              (1)、可能找到,也可能找不到
              (2)、因为lower_bound返回的是数组中第一个大于等于Node{0,0,t}的位置,有三种可能:
                1. 命中,找到sum=t,现在p就是结果位置
                2. 没有命中,返回的是sum>t的第一个位置,这不是我们想要的结果,需要判断一下这样的情况。
                3. 没有命中,返回的是数组外边的一个空地,也是越界了也没有找到大于等于t的第一个位置,这不是我们想要的结果,需要判断一下这样的情况
            */
            if (p < idx && f[p].sum == t) {
                cout << a << ' ' << b << ' ' << f[p].c << ' ' << f[p].d << ' ' << endl;
                return 0;
            }
        }
    }
    return 0;
}

四、手写版本二分

#include <bits/stdc++.h>
using namespace std;

const int N = 5e6 + 10;

struct Node {
    int c, d, sum;
    bool operator<(const Node &t) const {
        if (sum != t.sum) return sum < t.sum;
        if (c != t.c) return c < t.c;
        return d < t.d;
    }
} f[N];

int n;
int idx;

int main() {
    scanf("%d", &n);

    // C预处理出 c^2+d^2
    for (int c = 0; c * c <= n; c++)
        for (int d = c; c * c + d * d <= n; d++)
            f[idx++] = {c, d, c * c + d * d};

    //结构体排序
    sort(f, f + idx);

    //枚举a^2+b^2
    for (int a = 0; a * a <= n; a++) {
        for (int b = a; a * a + b * b <= n; b++) {
            int t = n - a * a - b * b;

            //手写二分模板,左闭右开
            // STL二分缺点:
            // 1、常数较大,速度慢
            // 2、对于结构体二分,需要构造空的Struct,还需要有一些玄学的赋零操作,不推荐
            //结论:全面采用手写二分办法,忘记STL的二分写法
            int l = 0, r = idx;
            while (l < r) {
                int mid = (l + r) >> 1;
                if (f[mid].sum >= t)
                    r = mid;
                else
                    l = mid + 1;
            }
            if (f[l].sum == t) {
                cout << a << ' ' << b << ' ' << f[l].c << ' ' << f[l].d << ' ' << endl;
                exit(0);
            }
        }
    }
    return 0;
}

五、\(STL\)的\(Hash\)表

#include <bits/stdc++.h>
using namespace std;

typedef pair<int, int> PII;
#define x first
#define y second

// TLE
// 通过了 8/11个数据
unordered_map<int, PII> _map;

int main() {
    int n;
    cin >> n;
    for (int c = 0; c * c <= n; c++)
        for (int d = c; d * d + c * c <= n; d++) {
            int t = c * c + d * d;
            if (_map.count(t) == 0)
                _map[t] = {c, d};
        }

    for (int a = 0; a * a <= n; a++)
        for (int b = a; b * b + a * a <= n; b++) {
            int t = n - a * a - b * b;
            if (_map.count(t)) {
                printf("%d %d %d %d", a, b, _map[t].x, _map[t].y);
                return 0;
            }
        }
    return 0;
}

六、桶排+二层循环+对边寻找

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
/*
用桶计数的思路
*/
// Accepted	134 ms
using namespace std;
const int N = 5e6 + 10;
int n;
int bucket[N];

int main() {
    scanf("%d", &n);

    memset(bucket, -1, sizeof bucket);

    for (int c = 0; c * c <= n / 2; c++)
        for (int d = c; c * c + d * d <= n; d++) {
            int t = c * c + d * d;
            if (t > 5e6) continue;
            if (bucket[t] == -1) bucket[t] = c;
        }

    for (int a = 0; a * a <= n / 4; a++) {
        for (int b = a; a * a + b * b <= n / 3; b++) {
            int t = n - a * a - b * b;
            int c = bucket[t];
            if (bucket[t] == -1 || t > 5e6) continue;
            int d = sqrt(t - c * c);
            printf("%d %d %d %d\n", a, b, c, d);
            exit(0);
        }
    }

    return 0;
}

标签:std,10,1221,int,平方和,using,include,AcWing
From: https://www.cnblogs.com/littlehb/p/16820538.html

相关文章

  • AcWing80 骰子的点数(线性dp)
    #definepbpush_backclassSolution{public:vector<int>numberOfDice(intn){intf[15][100];//投i次,总和为j的投掷可能memset(f,0,sizeof(......
  • AcWing 895.最长上升子序列Ⅰ
    题目链接:http://www.acwing.com/problem/content/897/浅浅复习放AC代码1#include<bits/stdc++.h>2usingnamespacestd;34constintN=1010;5intn;......
  • AcWing 154.滑动窗口
    AcWing154.滑动窗口题目描述给定一个大小为n≤10^6的数组。有一个大小为k的滑动窗口,它从数组的最左边移动到最右边。你只能在窗口中看到k个数字。每次滑动窗口......
  • 2022下半年 Acwing 第二篇:归并模板
    归并其实和快排比较类似,所以模板的记忆也大差不差。不能省懒!voidmerge_sort(intq[],intl,intr){if(l>=r)return;intmid=l+r>>1;merge_s......
  • ACWing 可达性统计
    ACWing可达性统计bitset可以说是一个多位二进制数,每八位占用一个字节,因为支持基本的位运算,所以可用于状态压缩,n位bitset执行一次位运算的时间复杂度可视为n/32.bitset<......
  • acwing 分组背包问题
    题面有N组物品和一个容量是V的背包。每组物品有若干个,同一组内的物品最多只能选一个。每件物品的体积是vij,价值是wij,其中i是组号,j是组内编号。求解将哪些物品......
  • acwing 混合背包问题
    题面有N种物品和一个容量是V的背包。物品一共有三类:第一类物品只能用1次(01背包);第二类物品可以用无限次(完全背包);第三类物品最多只能用si次(多重背包);每种体积是v......
  • acwing 二维费用的背包问题
    题面有N件物品和一个容量是V的背包,背包能承受的最大重量是M。每件物品只能用一次。体积是vi,重量是mi,价值是wi。求解将哪些物品装入背包,可使物品总体积不超过背......
  • 2022下半年 Acwing 第一篇:快排模板
    模板内容:C++voidquick(intq[],intl,intr){if(l>=r)return;intx=q[(l+r+1)>>1],i=l-1,j=r+1;while(i<j){doi++;while(q[i]<x);......
  • AcWing1251 打击罪犯--并查集
    #include<bits/stdc++.h>#definepbpush_back#definefifirst#definesesecond#defineall(x)(x).begin(),(x).end()#defineSZ(x)(int)(x).size()usingnam......