Missing Numbers(平方数组 贪心)

原题: http://codeforces.com/contest/1081/problem/E

题意:

有一个n(偶数)长的数组,每一个前缀和都是一个平方数,现在给出偶数位上的数,求原数组。(output any)

解析:

设原数组为{a,b,c,d},那么a是一个平方数,a+b是一个平方数,a+b+c是平方数,其中b已知,a,c可自己调节,那么如果a+b+c已经确定,则a+b越小越好,大了可以超过a+b+c的预设值了。所以可以贪心,找最小的a使a为平方数且a+b是平方数。

对于a分析:找一个最小的平方数a,使a+b为平方数
对于c分析:已有a+b,找一个最小的c,使a+b+c为平方数,因为c可以调度,所以问题变成找最小的大于a+b的平方数x使x+d为平方数

对于第一个数分析,设a[1]=a^2,a[1]+a[2]=x^2,那么a[2]=x^2-a^2=(x+a)(x-a),显然,a[3]是可以调节的,所以a[1]+a[2]越小越好,即x^2越小越好,x=((x+a)+(x-a))/2,也就是将a[2]分成两个数p,q的乘积,且p+q越小越好,那么|p-q|越小越好,即将a[2]分成两个差较小的数的乘积,那么从 a [ 2 ] \sqrt{a[2]} 开始往下找即可。

对于第三个数分析,设m=a[1]+a[2],c=a[3],x^2=m+c,d=a[4],其中m和d已知:
c>=1,所以知道了x的下限: m + 1 \sqrt{m+1}
x 2 + d = y 2 d = ( y x ) ( y + x ) ( y + x ) ( y x ) > = 2 m + 1 x^2+d=y^2\\d=(y-x)(y+x)\\(y+x)-(y-x)>=2\sqrt{m+1}
也就是说,除了|p-q|需要较小以外,还需要 p q > = 2 m + 1 |p-q|>=2\sqrt{m+1}

还有,p和q是(y-x)和(y+x),所以一定是同号的,那么d为偶数的时候,就一定是两个偶数的乘积,那么如果d不能整除4,一定不行。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define debug(i) printf("# %d\n",i)

LL a[100009];

LL deal1(LL val){
    LL i=(LL)sqrt(val);
    if(val%2)
    for(i-=(1-i%2);i>=1;i-=2){//两个奇因子
        if(val%i==0){
            LL x=val/i-i;x/=2;
            if(x*x<=0||x*x>1e13)continue;
            return x*x;
        }
    }
    else
    for(i-=i%2;i>=2;i-=2){//两个偶因子
        if(val%i==0&&val/i%2==0){
            LL x=val/i-i;x/=2;
            return x*x;
            if(x*x<=0||x*x>1e13)continue;
        }
    }
    return -1;
}

LL deal(LL m,LL val){
    LL sub=2*(LL)sqrt(m+1);
    LL i=(LL)sqrt(val);
    if(val%2)
    for(i-=(1-i%2);i>=1;i-=2){//两个奇因子
        if(val%i==0&&(val/i-i>=sub)){
            LL x=val/i-i;x/=2;
            LL c=x*x-m;
            if(c<=0||c>1e13)continue;
            return c;
        }
    }
    else
    for(i-=i%2;i>=2;i-=2){//两个偶因子
        if(val%i==0&&val/i%2==0&&(val/i-i>=sub)){
            LL x=val/i-i;x/=2;
            LL c=x*x-m;
            if(c<=0||c>1e13)continue;
            return c;
        }
    }
    return -1;
}

int main(){
    int n;scanf("%d",&n);
    bool cant=0;
    for(int i=2;i<=n;i+=2){
        scanf("%lld",a+i);
        if(a[i]%2==0&&a[i]%4)cant=1;
    }
    if(cant)return 0*printf("No\n");

    a[1]=deal1(a[2]);
    if(a[1]==-1)return 0*printf("No\n");

    LL sum=a[1]+a[2];
    for(int i=3;i<=n;i+=2){
        a[i]=deal(sum,a[i+1]);
        if(a[i]==-1){
            return 0*printf("No\n");
        }
        sum+=a[i]+a[i+1];
    }
    printf("Yes\n");
    for(int i=1;i<=n;i++)printf("%lld%c",a[i],(i==n?'\n':' '));
}

猜你喜欢

转载自blog.csdn.net/jk_chen_acmer/article/details/85114322