题目大意
题意:给你有 n n n 个数字的一个数列,问最多有多少个数字他们 两两的差是 2 的幂次方数。
思路
首先,我们想一个数能不能组成一个满足题目要求的序列,答案是肯定的。(直接输出
a
1
a_1
a1 不就彳亍了吗)
再想两个数彳亍不彳亍。看看样例:
输入 #1
6
3 5 4 7 10 12
输出 #1
3
7 3 5
举个栗子: 3 3 3 和 5 5 5 之间差距是 2 2 2,即 2 1 2^1 21。
再来看三个数的,样例输出就已经给了。
看四个数,还是举个栗子:
输入:
1 2 3 4
发现如果是四个数, 1 1 1 和 4 4 4 相差 3 3 3,并不是 2 2 2 的次方,发现不行。
证明一下:
令四个数为 a , b , c , d a,b,c,d a,b,c,d,则:
∣ a − b ∣ = 2 x 0 , ∣ b − c ∣ = 2 x 1 , ∣ c − d ∣ = 2 x 2 |a-b|=2^{x_0}, |b-c|=2^{x_1}, |c-d|=2^{x_2} ∣a−b∣=2x0,∣b−c∣=2x1,∣c−d∣=2x2
此时需要满足两两之差为 2 2 2 的次方,则 x 0 = x 1 = x 2 x_0=x_1=x_2 x0=x1=x2
∴ ∣ a − c ∣ = 2 x 0 + 1 , ∣ b − d ∣ = 2 x 1 + 1 |a-c|=2^{x_0+1}, |b-d|=2^{x_1+1} ∣a−c∣=2x0+1,∣b−d∣=2x1+1
∴ ∣ a − d ∣ = 3 × 2 x 0 ≠ 2 k |a-d|=3 \times 2^{x_0} \ne 2^k ∣a−d∣=3×2x0=2k
所以,可以满足题目要求的序列的元素个数只能输 1 , 2 , 3 1, 2, 3 1,2,3 个。
代码核心部分:
for (int i = 1;i <= n;i++) //枚举3个数组成的序列
{
for (int j = 0;j <= 30;j++)
{
int w = sum[j]; //w获取了2的j次方
if (s.count(a[i]+w) >= 1 && s.count(a[i]+2*w) >= 1) //判断是否有一个满足条件的序列
{
printf("3\n%lld %lld %lld",a[i],a[i]+w,a[i]+2*w); //可以输出啦
return 0; //直接结束程序
}
}
}
最后,附上我的 AC 代码:
#include <bits/stdc++.h> //万能头
#define int long long //可能会超int
using namespace std;
int n;
int a[200010];
set <int> s; //用集合去存储,会自动去重和排序
int sum[40]; //sum[i]表示2的i次方
signed main() //这里注意是signed
{
sum[0] = 1; //这里初始化是为了优化pow函数
for (int i = 1;i <= 30;i++)
{
sum[i] = sum[i-1] * 2;
}
scanf("%lld",&n); //以防万一,就用scanf吧
for (int i = 1;i <= n;i++)
{
scanf("%lld",&a[i]);
s.insert(a[i]); //将a[i]加入集合
}
for (int i = 1;i <= n;i++) //枚举3个数组成的序列
{
for (int j = 0;j <= 30;j++)
{
int w = sum[j]; //w获取了2的j次方
if (s.count(a[i]+w) >= 1 && s.count(a[i]+2*w) >= 1) //判断是否有一个满足条件的序列
{
printf("3\n%lld %lld %lld",a[i],a[i]+w,a[i]+2*w); //可以输出啦
return 0; //直接结束程序
}
}
}
for (int i = 1;i <= n;i++) //枚举2个数组成的序列
{
for (int j = 0;j <= 30;j++)
{
int w = sum[j]; //w获取了2的j次方
if (s.count(a[i]+w) >= 1) //判断是否有一个满足条件的序列
{
printf("2\n%lld %lld",a[i],a[i]+w); //可以输出啦
return 0; //直接结束程序
}
}
}
printf("1\n%lld",a[1]); //最后,如果3个或2个都不行,那就随便输出原数列中的一个数
return 0;
}