hihocoder1506序列的值

题目: 戳这里

题意:有一个长度为n(n <= 100000)的序列a[1.. n],对于任一子序列b[1..m],有f(b[1...m])等于有多少个i (0<=i<m)使得(0^b[1]^b[2]^....^b[i])<(0^b[1]^b[2]^....^b[i]^b[i+1]),然后求a[1....n]所有子序列的值之和。

思路:这个问题可以转化成:对于a[i]来说,可以从a[1...n]中抽出多少个子序列(子序列中包含a[i]),使得a[i]^S(S是子序列中a[i]前面的部分的异或和)>S,然后累加每个a[i]的贡献就是答案了。

想要使a[i]^S>S,只能在a[i]二进制最高位是1的那一位,S的对应位是0,否则无法使a[i]^S>S。所以a[i]的前半部分的数量等于a[i]的最高位对应的哪一位异或和是0的子序列个数,这个可以用dp维护,定义dpij 表示第i位所有的异或和是j的子序列个数。然后用a[i]的对应位转移一下就可以了。a[i]的后半部分可以任意取,所以a[i]的贡献=前半部分的方案数*2^(n-i);

代码:

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

const ll mod = 998244353;
const int maxn = 1e5 + 10;
ll pow1[maxn];
ll a[maxn], n;
ll dp[100][2];
void init()
{
    pow1[0] = 1;
    for(int i = 1; i < maxn; i++) pow1[i] = pow1[i - 1] * 2%mod;
}

int main()
{
    init();
    scanf("%lld",&n);
    memset(dp,0,sizeof(dp));
    for(int i = 1; i <= n; i++)
        scanf("%lld",&a[i]);
    ll ans = 0;
    for(int i = 0 ; i <= 32; i++) dp[i][0] = 1;
    for(int i = 1; i <= n; i++)
    {
        int bit;
        for(bit = 31 ; ((a[i]>> bit)&1)==0; bit--);
        (ans += ((dp[bit][0]* (ll)pow1[n - i])%mod))%=mod;
        for(bit = 0 ; bit <= 32; bit++)
        {
            ll l = dp[bit][0];
            ll r = dp[bit][1];
            ll x = (a[i]>>bit)&1;
            if(x)
            {
                dp[bit][0] = (l + r)%mod;
                dp[bit][1] = (l + r)%mod;
            }
            else
            {
                dp[bit][0] = l * 2%mod;
                dp[bit][1] = r * 2%mod;
            }
        }
    }
    printf("%lld
",ans);
}
View Code
原文地址:https://www.cnblogs.com/rtyfcvb/p/7085890.html