CF1879D Sum of XOR Functions

异或和按位处理的典型例题。要求所有子区间异或和乘区间长度的总和,朴素的方法是 O(n2) 地枚举区间,显然无法通过。

因为涉及异或和,而异或运算不进位,故自然地想到ai 写成二进制形式,单独研究每一位的贡献,最后再合并。这是处理此类问题的一般思路。

1. 二进制拆分

比方说,对于如下样例,我们把 ai 写成二进制形式:

a[1] = 5  = 0101
a[2] = 11 = 1011
a[3] = 7  = 0111
a[4] = 4  = 0100

0 位可以提出来,变成一个 01 串 1110,第 1 位提出来,得到 0110……

2. 对每一位求贡献

二进制拆分后,问题也就转化成了:对每个 01 串求所有含有奇数个 1 的区间的区间长度和 res(因为只有含奇数个 1 的区间,异或和才为 1,长度才会被计入贡献)。

下面用 dp 求 res

要研究区间,往往通过前缀来转化。有哪些情况能使得区间 [l,r] 有奇数个 1 呢?

  • 如果区间 [1,r] 有奇数个 1,区间 [1,l] 有偶数个 1,则区间 [l,r] 有奇数个 1
  • 如果区间 [1,r] 有偶数个 1,区间 [1,l] 有奇数个 1,则区间 [l,r] 有奇数个 1

由此,我们

  • cnti,0/1 表示在 [1,i] 中,有多少个前缀含有偶数/奇数个 1
  • sumi,0/1 表示在 [1,i] 中,含有偶数/奇数个 1 的前缀总长度是多少;
  • leni 表示恰好以 i 结尾的含有奇数个 1 的区间的总长度,显然 res=leni

于是有

leni=cnti1,0×isumi1,0,若 [1,i] 有偶数个 1;
leni=cnti1,1×isumi1,1,若 [1,i] 有奇数个 1

这实质上是把所有前缀和作差求区间和的操作放在一起做。

3. 合并每一位统计答案

最终的答案 ans=i=030resi×2i

void solve() {
    int n, ans = 0;
    cin >> n;
    vector<int> a(n + 1), b(n + 1);
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    for (int t = 0; t <= 30; t++) {
        for (int i = 1; i <= n; i++) {
            b[i] = (a[i] >> t) & 1;
        }
        int x = 0, res = 0;   // the number of 1s
        int cnt[2] = {1, 0};  // the number of the intervals
        int sum[2] = {0, 0};  // the sum of the intervals' lengthes
        for (int i = 1; i <= n; i++) {
            x = (x + b[i]) % 2;
            res = (res + (LL)cnt[1 - x] * i % mod - sum[1 - x] + mod) % mod;
            cnt[x]++, sum[x] = (sum[x] + i) % mod;
        }
        ans = (ans + (LL)res * ((1 << t) % mod) % mod) % mod;
    }
    cout << ans << endl;
}
posted @   XYukari  阅读(155)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示