洛谷 P3917 异或序列

Description

给出序列 A 1 , A 2 , ⋯   , A N A_1,A_2,\cdots,A_N A1,A2,,AN,求

∑ 1 ≤ i ≤ j ≤ N A i ⨁ A i + 1 ⨁ ⋯ ⨁ A j \sum_{1\le i\le j\le N} A_i\bigoplus A_{i+1}\bigoplus\cdots\bigoplus A_j 1ijNAiAi+1Aj

的值。其中, ⨁ \bigoplus 表示按位异或。

Input

第1 行,1 个整数 N N N

第2 行, N N N 个整数 A 1 , A 2 , ⋯   , A N A_1,A_2,\cdots,A_N A1,A2,,AN

Output

一个数,为表达式的值。

Solution

1.可以先将n个数拆分成二进制,处理后的数组进行前缀异或处理,得到前缀异或数组。

2.得到处理后,对于区间[L, R]的异或值,我们可以用XOR[L-1] ^ XOR[R]得到

3.基于此理论,对于每一位,该位上对答案的贡献即为[0, n]上0的个数与1的个数与该位的基值之积

解释:

基于基本知识 0 ^ 1 = 1,求出0与1的个数,也就求出了前缀异或区间为0和1的区间,这两个区间作异或处理,得到的值为1,对答案有贡献。

特殊处理:

包含0这个特殊下标,需要将其加入到答案中,

不然会忽略掉所有1区间前方的异或值为1的区间。

Code

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

#define MOD 1000000007
#define intmax 2147483647
#define memmax 0x7fffffff

inline ll read()
{
    
    
    ll x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9')
    {
    
    
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
    {
    
    
        x = x * 10 + ch - 48;
        ch = getchar();
    }
    return x * f;
}

ll n;
ll a[100005];
ll num[100005][35];

void solve()
{
    
    
    n = read();
    for (int i = 1; i <= n; i++)
    {
    
    
        a[i] = read();
        for (int j = 0; j < 32; j++)
            num[i][j] = (a[i] >> j) & 1;
    }
	
    // 构建前缀异或数组
    for (int i = 1; i <= n; i++)
        for (int j = 0; j < 32; j++)
            num[i][j] ^= num[i - 1][j];

    ll ans = 0;
    ll base = 1;
    for (int j = 0; j < 32; j++)
    {
    
    
        ll num0 = 0, num1 = 0;
        for (int i = 0; i <= n; i++)
        {
    
    
            if (num[i][j])
                num1++;
            else
                num0++;
        }
        ans += num0 * num1 * base;

        base <<= 1;
    }

    cout << ans;

    
}

int main()
{
    
    
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    solve();
}

猜你喜欢

转载自blog.csdn.net/weixin_45812280/article/details/121159331