题解:P14074 [GESP202509 五级] 有趣的数字和

洛谷 P14074 题解

题目传送门

解题思路

大多数人都会选择枚举 \([l,r]\) 之间的整数,看是否是有趣的,但是肯定会超时

\([l,r]\) 之间有趣数字和十分困难,但是求 \([1,l]\) 之间的有趣数字和却能发现规律

我们可以根据一些例子找规律,如:\({8,9,10,11}\)

二进制形式分别为 \({1000, 1001, 1010, 1011}\)

我们发现数字前面都有“10”,数字和是1(奇数),那么 \({8, 11}\) 是有趣的数字

否则就是 \({9, 10}\)

我们多举几个例子不难发现,\({4x,4x+1,4x+2,4x+3}\) 几个数字,高位肯定有一段相同的地方,如果那段地方数字和是奇数,则 \({4x,4x+3}\) 是有趣的数字,否则另两个数字是有趣的

所以,\({4x,4x+1,4x+2,4x+3}\) 之间的有趣数字之和一定是 \(8x+3\),我们就可以用这个规律计算出 \([1, 4k+3]\) 之间好数字之和

\[8 \times \frac{(k-1)\times k}{2} = 4k^2 - k \]

接着枚举其他没有计算到的数(绝对不会超过4个)即可

不要忘了,我们求的是以1为开头的,所以我们可以设一个函数 \(f(n)\) 表示 \([1,n]\) 之间的有趣数字和

最后答案等于 \(f(r)-f(l-1)\),类似前缀和的思路

Code

#include <bits/stdc++.h>
#define int long long
using namespace std;
int s[16] = {0, 1, 3, 3, 7, 7, 7, 14, 22, 22, 22, 33, 33, 46, 60, 60}; //打表出省一
inline int sum(int n) //计算一个数的二进制位数位之和
{
    if (n == 1)
    {
        return 1;
    }
    int cnt = 0; //数位之和
    for (int i = 0; i <= 30; i++) //30位以内
    {
        if ((n >> i) & 1)
        {
            cnt++;
        }
    }
    return cnt;
}
inline int f(int n) //计算[1,n]的有趣数字和
{
    if (n < 16)
    {
        return s[n]; //可爱的打表
    }
    int b = ((n - 4) >> 2); //b为计算完整区间的数量相关参数
    int a = ((b * b) << 2); //a为最终的答案(初始值,完整区间的和)
    a -= b;
    for (int i = (b << 2); i <= n; i++)
    {
        if (sum(i) & 1) //二进制数字和为奇数
        {
            a += i; //加上i
        }
    }
    return a;
}
signed main()
{
    int l, r;
    cin >> l >> r; //输入
    cout << f(r) - f(l - 1) << endl; //输出
    return 0;
}
posted @ 2025-10-03 09:57  fengjunxiao2014  阅读(26)  评论(0)    收藏  举报