1 条题解

  • 0
    @ 2026-8-6 23:01:01

    首先我们先构造一条直线上最多能放几个点。先将这条直线上的点的贡献算出来。

    我们设直线上有 lenlen 个点,那么产生的贡献就是 len×(len1)2\frac{len \times (len-1)}{2}。并且由于 lenlen 极大,剩余需要的贡献不超过 lenlen,记为 resres

    那么我们就可以排列一下直线上的点的分布方式,新增一个点,使得其正好和直线上 resres 个点距离为整数。

    也就是说,对于这个新点,我们设他离直线的距离为 disdis,那么这个 disdis 必须至少和 resres 个数构成勾股数的前两个。

    这时候既可以打表也可以公式算出这个 disdis。如果使用公式那么有:(ab)2+(2ab)2=(a+b)2(a-b)^2+(2ab)^2=(a+b)^2,我们从 2ab2ab 下手。也就是说我们要让 disdis 有较多的因子。这里采用 510510510510 作为 disdis,即 $2 \times 3 \times 5 \times 7 \times 11 \times 13 \times 17$。

    接下来就要决定直线上每个点的位置。我们固定直线的 yy 坐标为 00,固定新点坐标 (0,dis)(0,dis)。令直线上 resres 个点的 xx 坐标能和 disdis 构成勾股数的前两个,剩下的 lenreslen-res 个点则不能。最后还有 nlen1n-len-1 个点没有用上,这里直接全部流放到角落。

    下附代码。

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #define int long long
    #define PII pair<int,int>
    using namespace std;
    int n,k,dis=510510;
    bool check(int a,int b){//判断能否构成勾股数
        int c=sqrt(a*a+b*b);
        if(c*c==a*a+b*b) return 1;
        return 0;
    }
    signed main(){
        cin>>n>>k;
        if(k==n*(n-1)/2){
            for(int i=1;i<=n;i++) cout<<"0 "<<i<<endl;
            return 0;
        }
        if(k==0){
            for(int i=1;i<=n;i++) cout<<i<<' '<<i<<endl;
            return 0;
        }
        int len=1;
        while(len*(len+1)/2<=k-1) len++;
        int res=k-len*(len-1)/2;//res为需要的贡献
        cout<<"0 "<<dis<<endl;//用来组成勾股数的新点
        int x=1;
        for(int i=1;i<=res;i++){//寻找能组成勾股数的坐标
            while(!check(x,dis)) x++;
            cout<<x<<' '<<0<<endl;x++;
        }
        for(int i=1;i<=len-res;i++){//剩下的点不能产生贡献
            while(check(x,dis)) x++;
            cout<<x<<' '<<0<<endl;x++;
        }
        for(int i=len+2;i<=n;i++) cout<<1000000000-i<<' '<<-1000000000+i<<endl;
        return 0;
    }
    
    • 1

    信息

    ID
    12556
    时间
    1000ms
    内存
    700MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者