2021牛客暑期多校训练营3 E题

E题: Math

原题链接:https://ac.nowcoder.com/acm/contest/11254/E

题目大意

给定 n ( 1 ≤ n ≤ 1 0 18 ) n(1\le n\le 10^{18}) n(1≤n≤1018) ,求满足 x y + 1 ∣ x 2 + y 2 xy+1|x^2+y^2 xy+1∣x2+y2 的正整数对 ( x , y ) ( 1 ≤ x ≤ y ≤ n ) (x,y)(1\le x\le y\le n) (x,y)(1≤x≤y≤n) 的数量。

题解

通过打表查找小范围内的数对,我们不难发现以下两条规律:
①:若不存在小于 x x x 的 y y y 可与 x x x 配对,则与 x x x 可配对的最小 y y y 是 x 3 x^3 x3 ,例如:
2—8
3—27
4—64
②:对于非 ( x , x 3 ) (x,x^3) (x,x3) 形式的数对,其呈现多组的 x x x 与 y y y 首位相同连成序列的形式,且首位呈 3 3 3 次幂的形式,例如:
8( 2 3 2^3 23 )—30—112—418—1560—5822—…
27( 3 3 3^3 33 )—240—2133—…
64( 4 3 4^3 43 )—1020—…
显然同时考虑两者,我们可以发现对于一条序列(将满足①的数对接到②的序列前方,如2—8—30—112—418—1560—5822—…),其前两项必然为 ( x , x 3 ) (x,x^3) (x,x3) ,对于第二项开始,每一项 x x x 都有 2 2 2 个可以搭配组成合法数对的 y y y (一大一小,可以通过解关于 y y y 的一元二次方程 y 2 − k x y + x 2 − k = 0 y^2-kxy+x^2-k=0 y2−kxy+x2−k=0 证明有 2 2 2 个不同的解)。
对于为何前两项必然为 ( x , x 3 ) (x,x^3) (x,x3) 的形式和为何对于首位 x x x 为何仅有 x 3 x^3 x3 一个合法解,在此不给出 (给不出) 具体证明(属于是只会通过打表找规律的蒟蒻)。
对于编写代码解题而言,我们只需要对于每一个自然数找出其往后延伸的序列,然后将序列内每相邻 2 2 2 个数组成的合法数对存储下来即可。
对于式子 x y + 1 ∣ x 2 + y 2 xy+1|x^2+y^2 xy+1∣x2+y2 ,我们将其写为 k ( x y + 1 ) = x 2 + y 2 k(xy+1)=x^2+y^2 k(xy+1)=x2+y2 的形式。
若我们知道 x x x 与 k k k 的值,上式可化简为关于 y y y 的一元二次方程 y 2 − k x y + x 2 − k = 0 y^2-kxy+x^2-k=0 y2−kxy+x2−k=0 ,由韦达定理易得 k x = y 1 + y 2 kx=y_1+y_2 kx=y1​+y2​ ,改写作 y 2 = k x − y 1 y_2=kx-y_1 y2​=kx−y1​ 的形式,我们就得到了关于一条序列的一般递推式(对于我们当前处理的序列,我们已知前两项 ( x , x 3 ) (x,x^3) (x,x3) ,那么我们就可以代入算得 k = x 2 k=x^2 k=x2 ,并利用 x = x , y = x 3 x=x,y=x^3 x=x,y=x3 这组合法解开始递推)。

参考代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll MAXN=1e18+5;
int t;
ll n,ans[2000005],cnt;
int main()
{
    std::ios::sync_with_stdio(false),cin.tie(0);
    ll i,j,x,y,k,p;
    ans[cnt++]=1;//第一条序列为1—1—1—1—1—1—1—1—...,特判提前放入
    for(i=2;i<=1000000;i++){
        x=i,y=x*x*x,k=y/x;//对于每个数初始计算好k与第一组(x,y)
        ans[cnt++]=y;//存储时放入数对中的较大值,方便二分查找
        while(y<=(MAXN+x)/k){//控制递推,超过数据范围即不存在下一组解
            p=y;
            y=k*y-x;//计算新的y2,因为此时(x,y)的大小关系翻转,所以要换位
            x=p;//将原来的y的值赋给x
            ans[cnt++]=y;//存储时放入数对中的较大值,方便二分查找
        }
    }
    sort(ans,ans+cnt);
    cin>>t;
    while(t--){
        cin>>n;
        printf("%d\n",upper_bound(ans,ans+cnt,n)-ans);//二分查找第一个超过n范围的非法数对,取得下标即是合法数对数量
    }
    return 0;
}
上一篇:HDU 6514 前缀和


下一篇:两圆交点