从CF1879D学习一类区间贡献题思路

https://codeforces.com/contest/1879/problem/D

关键在于互换两个 ​ 的顺序

一般像这样计算所有子区间的式子,如果要优化成接近线性,有一种可行思路是把注意力放在右端点,通过不断移动右端点,在移动的时候利用前面的统计结果算出移动右端点后的结果,从而得出所有子区间的答案。然后还有一个显然有用的套路是前缀和,这里记 si=a1a2···an1ansi,jsi 二进制的第 j 位,我们会发现 l=1nr=lnf(l,r)=i=032l=1nr=lnsl,isr,i×2i

由于上面的式子可以优化成 O(nlogV),我们受到启发推出下面的式子

l=1nr=lnf(l,r)×(rl+1)

=r=1nl=1rf(l,r)×(rl+1)()

=r=1nl=1ri=032sl,isr,i×2i×(rl+1)

=r=1ni=032l=1r[sr,isl,i=1]×2i×(rl+1)

=r=1ni=0322i×l=1r([sr,isl,i=1]×r[sr,isl,i=1]×(l1))

r​ 视作常量再观察上面的式子,你就会发现最后一维可以被优化掉,因为 l=1r[sr,isl,i=1]×r​ 和 l=1r[sr,isl,i=1]×(l1)​ 都可以预处理。

void solve() { // #define tests int n; std::cin >> n; std::vector<int> a(n); for (auto& x : a) {std::cin >> x;} PrefixXor<i64> pre_xor(a); std::vector cnt(2, std::vector<Z>(31)); auto sum(cnt);//维护该数之前该位上为0/1的个数 cnt[0].assign(31, 1);//一开始0的个数赋成1 Z ans = 0; for (int r = 1; r <= n; r++) {//固定r,更新答案 for (int j = 30; j >= 0; j--) { int x = (pre_xor(r) >> j & 1); ans += (cnt[x ^ 1][j] * r - sum[x ^ 1][j]) * (1 << j);//找当前位相反的数码的数量,只用这样才能产生贡献,即异或和为 1 sum[x][j] += r; cnt[x][j] += 1; } } std::cout << ans << '\n'; }

__EOF__

本文作者Kdlyh
本文链接https://www.cnblogs.com/kdlyh/p/18249434.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   加固文明幻景  阅读(26)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示