题目: 戳这里
题意:有一个长度为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); }