前置知识

\(\text{popcount}(n)\) 表示将 \(n\) 转为二进制后的数中 \(1\) 的个数。

结论

\[\sum_{i=1}^{n} \text{ popcount}(i)=\sum_{i=1}^{\left \lceil \log_{2}{n} \right \rceil-1 } \left [ (n>>(i-1))\text{&}1==1 \right ] \times (i\times 2^{i-1}+2^{i}\times \text {popcount}(n>>i)
\]

其中 $ \left [ (n>>(i-1))\text{&}1==1 \right ]$ 表示 \(n\) 转成二进制以后第 \(i\) 位是不是 \(0\)。

原理

首先我们需要知道这个东西:

__builtin_popcount(x)

可恶怎么又是 STL

他的作用就是求出 \(x\) 的 \(\text{popcount}\) 值,这个东西好像很快我们先把他当作 \(O(1)\) 的。

接下来我们考虑用 \(O(1)\) 的时间来求得

\[\sum_{i=0}^{2^{k}-1} \text{popcount}(i)
\]

的做法。

这里以 $ \left [ 0,2^{5}-1 \right ] $ 为例。

先把所有的数都给列出来。

然后我们可以看到最低位的规律。

依次向后走。

我们可以看到每一位里面都是一半是 \(0\),一半是 \(1\)。

因此我们可以得到下面的公式:

\[\sum_{i=0}^{2^{k}-1} \text{popcount}(i)=k\times 2^{k-1}
\]

下面以 \((11010110)_{2}=(214)_{10}\) 为例。

其第一位为 \(1\),所以我们直接计算 \((00000000)_{2}\text{~}(01111111)_{2}\) 的 \(\text{popcount}\) 和,也就是 \(0\times 2^{7}+7\times 2^{6}\)。

其第二位为 \(1\),所以我们直接计算 \((10000000)_{2}\text{~}(10111111)_{2}\) 的 \(\text{popcount}\) 和,也就是 \(1\times 2^{6}+6\times 2^{5}\)。

其第三位是 \(0\),对答案没有贡献。

其第四位为 \(1\),所以我们直接计算 \((11000000)_{2}\text{~}(11001111)_{2}\) 的 \(\text{popcount}\) 和,也就是 \(2\times 2^{4}+4\times 2^{3}\)。

其第五位是 \(0\),对答案没有贡献。

其第六位为 \(1\),所以我们直接计算 \((11010000)_{2}\text{~}(11010011)_{2}\) 的 \(\text{popcount}\) 和,也就是 \(3\times 2^{2}+2\times 2^{1}\)。

其第七位为 \(1\),所以我们直接计算 \((11010100)_{2}\text{~}(11010101)_{2}\) 的 \(\text{popcount}\) 和,也就是 \(4\times 2^{1}+1\times 2^{0}\)。

其第八位是 \(0\),对答案没有贡献。

但其实我们只需要处理 \([0,n)\) 这个区间分段即可。

最后再加上 \(\text{popcount}((11010110)_{2})=5\)。

最终结果就是:

\[0\times 2^{7}+7\times 2^{6}+1\times 2^{6}+6\times 2^{5}+2\times 2^{4}+4\times 2^{3}+3\times 2^{2}+2\times 2^{1}+4\times 2^{1}+1\times 2^{0}+\text{popcount}((11010110)_{2})
\]
\[=448+256+64+16+9+5
\]
\[=798
\]

因为 \(\text{popcount}(0)=0\),所以统计不统计都可以。

代码

scanf("%d", &n);
long long tot = 0;
int cnt = 0;
int x = n;
while(x)
{
if(x & 1)
tot += (cnt * (1 << (cnt - 1))) + (1 << cnt) * __builtin_popcount(x >> 1);
x >>= 1;
cnt++;
}
tot += __builtin_popcount(n);
printf("%lld ", tot);

转载自:https://kaiserwilheim.github.io/OI/fast-popcnt-sum/

虽然是转载但是 \(\LaTeX\) 都是我自己打的QAQ

快速求popcount的和的更多相关文章

  1. 快速求n的质因子(数论)

    快速求n的质因子 如何尽快地求出n的质因子呢?我们这里又涉及两个好的算法了! 第一个:用于每次只能求出一个数的质因子,适用于题目中给的n的个数不是很多,但是n又特别大的 #include<std ...

  2. 【GDOI 2011 DAY2 T3】零什么的最讨厌了 (快速求阶乘、中国剩余定理)

    问题描述: 林记在做数学习题的时候,经常遇到这种情况:苦思冥想了很久终于把问题解出来,结果发现答案是0,久而久之林记在得到习题答案是0的时候就没有了做出一道难题的成就感.于是林记决定:以后出题,答案一 ...

  3. 快速求幂(Quick Exponentiation)

    接触ACM没几天,向各路大神求教,听说ACM主要是研究算法,所以便开始了苦逼的算法学习之路.话不多说,RT所示,学习快速求幂. 在头文件<math.h>或是<cmath>中,d ...

  4. hdu 2814 快速求欧拉函数

    /** 大意: 求[a,b] 之间 phi(a) + phi(a+1)...+ phi(b): 思路: 快速求欧拉函数 **/ #include <iostream> #include & ...

  5. NYOJ--102--次方求模(快速求幂取模)

    次方求模 时间限制:1000 ms  |  内存限制:65535 KB 难度:3   描述 求a的b次方对c取余的值   输入 第一行输入一个整数n表示测试数据的组数(n<100)每组测试只有一 ...

  6. javascript中快速求数组的全部元素的相加之和

    js中快速求数组的全部元素的相加之和: var arr = [1,2,3,4,5];var sum = eval(arr.join('+')); console.log(sum); 运行结果: 15

  7. HDU 2035 人见人爱A^B(二分求幂,快速求幂)

    题意:求A的B次方的后三位数字 思路1:常规求幂,直接取余求解 代码: #include<iostream> #include<cstdio> using namespace ...

  8. 快速求排列C(m,n)加取模

    快速求排列组合C(m,n)%mod 写在前面: 1. 为防止产生n和m的歧义,本博文一律默认n >= m 2. 本博文默认mod = 10^6+3 3. 本博文假设读者已知排列组合公式 C(m, ...

  9. 线性齐次递推式快速求第n项 学习笔记

    定义 若数列 \(\{a_i\}\) 满足 \(a_n=\sum_{i=1}^kf_i \times a_{n-i}\) ,则该数列为 k 阶齐次线性递推数列 可以利用多项式的知识做到 \(O(k\l ...

  10. Quick Pow: 如何快速求幂

    今天讲个有趣的算法:如何快速求 \(n^m\),其中 n 和 m 都是整数. 为方便起见,此处假设 m >= 0,对于 m < 0 的情况,求出 \(n^{|m|}\) 后再取倒数即可. ...

随机推荐

  1. PCB封装设计建议:

    1,通孔型元器件建议孔直径比元器件管脚直径大0.2-0.3mm左右,焊盘铜皮外沿一般是0.3-1mm(相当于直径应该加0.6-2mm)宽大元件可再大一点,对于设计单面板的,则最小铜皮外沿应大于1mm以 ...

  2. C# 将实体转xml/xml转实体

    xml转实体 /// <summary> /// 把xml转换成实体 /// </summary> /// <typeparam name="T"&g ...

  3. 更新kubeadm kubernets平台证书

    证书过期失效: Unable to connect to the server: x509: certificate has expired or is not yet valid 更新证书: 1.查 ...

  4. MFC 与 C++ 类型转换

    C++ 中到的类型转换很多,先记录下来,多了写成一个类 1.CString转int 1 CString cNum="9527"; 2 int iNum = 0; 3 iNum=at ...

  5. Linux高并发服务器之Linux多线程开发

    本文源自C++高薪面试项目的学习笔记,主要记录Liunx多线程的学习,主要知识点是线程概述等基础概念以外,还有线程相关Liunx系统函数以及对应练手代码,除此之外还有线程同步问题的讲解以及实战多线程买 ...

  6. 痞子衡嵌入式:盘点国内RISC-V内核MCU厂商(2021年发布产品)

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是国内RISC-V内核MCU厂商(2021). 虽然RISC-V风潮已经吹了好几年,但2019年才是其真正进入主流市场的元年,最近国内大量 ...

  7. Android笔记--案例:找回密码

    找回密码 具体实现: 登录成功: 报告密码不同: 报告验证码错误: 代码相关: 找回密码的界面很简单,不细说了,直接写就行 找回密码的逻辑实现: 下一次就去写数据存储啦! 拜拜!

  8. Android笔记--图像显示

    imageView 具体实现: 注意:图片名称应当为小写的英文字母与数字的结合,当然,二者可以只存在其一 图片的放置的比例:(通过scaleType属性设置) ImageButton ImageBut ...

  9. 股票数据Scrapy爬虫实例(亲测有效)

    步骤: 步骤1:建立工程和Spider模板 scrapy startproject BaiduStocks cd BaiduStocks scrapy genspider stocks baidu.c ...

  10. JetBrains 2022全家桶-激活

    ## JetBrains 全家桶 激活教程 https://tech.souyunku.com/?page_id=50199