每日一题之数字诗意

题描述

在诗人的眼中,数字是生活的韵律,也是诗意的表达。

小蓝,当代顶级诗人与数学家,被赋予了"数学诗人"的美誉。他擅长将冰冷的数字与抽象的诗意相融合,并用优雅的文字将数学之美展现于纸上。

某日,小蓝静坐书桌前,目光所及,展现着 nn 个数字,它们依次为 a1,a2,…,an,熠熠生辉。小蓝悟到,如果一个数能够以若干个(至少两个)连续的正整数相加表示,那么它就蕴含诗意。例如,数字 6 就蕴含诗意,因为它可以表示为 1+2+3。而 8 则缺乏诗意,因为它无法用连续的正整数相加表示。

小蓝希望他面前的所有数字都蕴含诗意,为此,他决定从这 n 个数字中删除一部分。请问,小蓝需要删除多少个数字,才能使剩下的数字全部蕴含诗意?

输入格式

第一行包含一个整数 n,表示展示的数字个数。

第二行包含 n 个整数 a1,a2,…,an​,表示展示的数字。

输出格式

输出一个整数,表示小蓝需要删除的数字个数,以使剩下的数字全部蕴含诗意。

#include <iostream>
#include <vector>
#include <cmath>
typedef long long ll;
using namespace std;

bool isShiyi(ll a) {
    // 判断一个数是否可以表示为连续自然数的和
    // 根据数学公式,a = k * (2 * m + k - 1) / 2,其中 k 是连续数的个数,m 是起始数
    // 我们可以通过遍历 k 来判断是否存在满足条件的 m
    for (ll k = 2; k * (k - 1) / 2 < a; ++k) {
        ll numerator = 2 * a - k * (k - 1);
        ll denominator = 2 * k;
        if (numerator % denominator == 0) {
            ll m = numerator / denominator;
            if (m > 0) {
                return true;
            }
        }
    }
    return false;
}

int main() {
    ll n;
    cin >> n;
    vector<ll> a(n);
    for (auto &x : a) {
        cin >> x;
    }
    ll cnt = 0;
    for (ll i = 0; i < a.size(); i++) {
        if (isShiyi(a[i])) {
            cnt++;
        }
    }
    cout << n-cnt;
    return 0;
}

这道题的思路很简单,判断一个数是否为连续自然数的和,但是数据量巨大,连续自然数之和,其实就是对一个等差数列求和,假设m 是起始的自然数,k 是连续自然数的个数,求和公式为

a=k⋅(2m+k−1)​/2,整理公式得m=(2a−k(k−1)​)/2k.

为了使 m 是自然数,必须满足以下条件:

  • (2a−k(k−1)​)/2k​ 是正整数,

  • m>0。

因此,我们需要找到满足以下条件的 kk:

  • 2a−k(k−1) 能被 2k 整除,

  • m=(2a−k(k−1)​)/2k>0。

所以我们根据给出的a和k,找到满足条件的m就好了。但是这种思路的时间复杂度很大。无法通过所有样例。

#include<bits/stdc++.h>  
using namespace std;

long long n, m, ans;  // 定义全局变量:n(输入的数的个数),m(当前输入的数),ans(符合条件的数的个数)

// 定义一个函数 chk,用于检查一个数是否是 2 的幂次方
bool chk(long long k) {
    int s = 0;  // 用于统计 k 的二进制表示中 1 的个数
    while (k) {  // 当 k 不为 0 时,继续循环
        s += k & 1;  // 将 k 的最低位(二进制)加到 s 中
        k >>= 1;     // 将 k 右移一位,相当于去掉最低位
    }
    if (s == 1)  // 如果 k 的二进制表示中只有一个 1
        return true;  // 返回 true,表示 k 是 2 的幂次方
    return false;  // 否则返回 false
}

int main() {
    cin >> n;  // 输入数的个数 n
    for (register int i = 1; i <= n; i++) {  // 使用 register 关键字声明循环变量 i,优化性能
        cin >> m;  // 输入当前数 m
        if (chk(m))  // 调用 chk 函数检查 m 是否是 2 的幂次方
            ans++;  // 如果是,ans 加 1
    }
    cout << ans;  // 输出符合条件的数的个数
    return 0;  
}

1. 连续自然数的和公式

假设一个数 S 可以表示为连续自然数的和,即:

S=m+(m+1)+(m+2)+⋯+(m+k−1)

其中:m 是起始的自然数,k 是连续自然数的个数。

根据等差数列求和公式,S 可以表示为:

S=k⋅(2m+k−1)

2S=2k⋅(2m+k−1)​

整理得:

2S=k⋅(2m+k−1)

2S=k⋅(2m+k−1)


2. 分析 S 是 2 的幂次方的情况

假设 S 是 2 的幂次方,即 S=2p,其中 p 是正整数。代入上式:

2⋅2p=k⋅(2m+k−1)

即:

2p+1=k⋅(2m+k−1)


3. 分析 kk 和 2m+k−12m+k−1 的性质

由于 2p+1 是 2 的幂次方,它的质因数分解中只有 2。因此,k 和 2m+k−1必须满足以下条件:

  • k 必须是 2 的幂次方,即 k=2q,其中 q 是正整数。

  • 2m+k−1也必须是 2 的幂次方,即 2m+k−1=2r,其中 r 是正整数。


4. 推导矛盾

将 k=2q 代入 2m+k−1=2r:

2m+2q−1=2r

整理得:

2m=2r−2q+1

因为 m 是自然数,所以 2r−2q+1必须是偶数。

然而:

  • 如果 q>0,则 2q 是偶数,2r−2q 是偶数,加上 1 后变为奇数。

  • 如果 q=0,则 k=1,此时 S=m,即 S 是一个自然数,但 S 是 2 的幂次方,且 S≥1。

因此,无论 q 如何取值,2m=2r−2q+1 都无法满足 m 是自然数的条件。

所以任何数是 2 的幂次方,那么这个数不能表示为连续自然数的和

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值