[luoguU42591][小T的面试题]

luoguU42591

题意:

n个不超过n的正整数中,其中有一个数出现了两次,其余的数都只出现了一次, 求这个出现两次的数。

思路:

这个题的亮点在于内存限制1MB。明显不能再用数组储存了,肯定是用一些运算来求出那个数。假设出现两次的数为x,没有出现的数为y。一开始很容易想到计算出1到n加起来应该得到的值,然后再将给出的数加起来。但是这样只能得到x-y的值。然后在这个基础上可以想到,用x-y不能推出这两个数是因为可能有很多数的差值与他们相同。但是如果用(x^2-y^2)所得到的差值重复的概率就会小很多。然后我再用这个差值依次去尝试每个y,看得到的x是不是一个完全平方数,如果是的话我们就假设所得到的x就是正确的。然而这样还是不保险怎们办。那就再用一开始的方法,再去判断,也就是现在我们已经知道这两个数是多少了,然后用实际的n个数的和加上他们的差值,看能否得到应得的值。

本来以为自己只是把这道题水过去了。后来仔细一想才发现,这也是有依据的,不是概率算法,而是正确解法。以下给出证明

重新考虑该题思路,其实本质上就是我用(x^2-y^2)的值和(x-y)的值推出了x与y的值。显然这是有唯一解的。假设存在一个$$c^2-d^2=x^2-y^2且c-d=x-y$$而又有$$c^2-d^2=(c-d)(c+d)$$$$x^2-y^2=(x-y)(x+y)$$就有$$(c-d)(c+d)=(x-y)(x+y)$$$$设k=c-d=x-y$$就有$$k+2d=k+2y$$$$所以d=y$$,然后就很容易得到c=x。所以x与y是唯一的。

代码:

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<bitset>
using namespace std;
typedef long long ll;
ll read() {
    ll x=0,f=1;char c=getchar();
    while(c<'0'||c>'9') {
        if(c=='-') f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9') {
        x=x*10+c-'0';
        c=getchar();
    }
    return x*f;
}
ll x;
ll x2;
ll sum,sum_;
int check(int x,int y) {
    return sum_-x+y==sum;
}
int main() {
    int n=read();
    for(ll i=1;i<=n;++i) {
        x+=i*i;
        sum+=i;
    }
    for(int i=1;i<=n;++i) {
        ll k=read();
        sum_+=k;
        x2+=k*k;
    }
    ll z=x-x2;
    for(ll i=1;i<=n;++i) {
        ll zz=i*i;
        if(zz+z<=0) continue;
        ll kk=sqrt(zz+z);
        if(kk*kk==zz+z) {
            if(!check(i,kk)) continue;
            cout<<i;
            return 0;
        } 
    }
    return 0;
}
原文地址:https://www.cnblogs.com/wxyww/p/9746071.html