1 条题解
-
0
自动搬运
来自洛谷,原作者为

Rainbow_qwq
**搬运于
2025-08-24 22:40:14,当前版本为作者最后更新于2022-07-30 23:46:54,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
来写一下 做法。
首先要有一个 的 simple 做法。考虑对于每个位,数 0 段的个数,然后用全部的减去这些。对每个位维护一个 表示当前结尾有多少个 0,那么这个位下一次是 就是操作 ,否则是 。可以写出如下代码。
int n; ull res,lst[66]; inline ull C2(int x){return 1ull*x*(x-1)/2;} signed main() { READ::init(n); ull sum=0; int lim=0; For(i,0,n-1){ ull x=READ::read(); sum+=(~x); For(j,0,63) if(x>>j&1)sum-=lst[j]<<j,lst[j]=0; else ++lst[j]; res-=sum; } res-=C2(n+1); cout<<res; return 0; }用一个 trick,考虑原本我们维护的是 64 个 ,且 是 的,把二进制数位画出来,这是一个 01 矩阵,并且只有 个位置可能有值。于是考虑转置这个矩阵,维护 表示哪些 的第 位为 1,这样只需要维护 个 。
对于 的操作就是把 的那些位变为 0;对于 的操作就手动模拟二进制加法器。代码如下。
小常数 ,1.6s。
int n; ull res; inline ull C2(int x){return 1ull*x*(x-1)/2;} ull w[30]; signed main() { READ::init(n); ull sum=0; int lim=0; For(i,0,n-1){ ull x=READ::read(); sum+=(~x); // For(j,0,63) // if(x>>j&1)sum-=lst[j]<<j,lst[j]=0; // else ++lst[j]; ull up=(~x),nup; for(int j=0;j<=lim;++j){ sum-=(w[j]&x)<<j; w[j]&=(~x); // 上两行是变为 0 的操作 nup=up&w[j]; w[j]^=up; up=nup; // 上三行是模拟 +1 的二进制加法器 } if((i&-i)==i)++lim; res-=sum; } res-=C2(n+1); cout<<res; return 0; }
- 1
信息
- ID
- 7643
- 时间
- 2077ms
- 内存
- 27MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者