根据异或匀算的性质和本题数据范围可以计算得最大子集异或和为 (1<<13)-1。
我们用dp[i][j]来表示状态,表示选择到第i个数后异或和为j的子集的个数。本题不能直接套01背包维度压缩,因为异或操作的结果并不是单调递增的。因为我们知道上一状态即可,之前的状态可以忽略所以我们可以开一个01滚动数组,防止二维数组过大爆堆。
利用异或运算的自反性(A XOR B XOR B = A xor 0 = A)可以直接找到一个X,j^a[i]=X,且
X^a[i]=j;
另外判断素数要判断到极限数据,也就是1e9,我们要在线性时间内完成这个操作。也就是说要使用线性筛素数来预处理所有素数。
状态转移方程
ACcode
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 1e9 + 7, N = 1 << 13;
signed main()
{
cin.tie(0)->sync_with_stdio(0);
int cnt = 0;
vector<int> prime(N + 1);
vector<bool> st(N + 1);
auto sieve = [&](int n)
{
st[1] = 1;
for (int i = 2; i <= n; i++)
{
if (!st[i])
prime[cnt++] = i;
for (int j = 0; i * prime[j] <= n; j++)
{
int t = i * prime[j];
st[t] = 1;
if (i % prime[j] == 0)
break;
}
}
};
sieve(N);
int n;
cin >> n;
vector<int> a(n + 1);
vector<vector<int>> dp(2, vector<int>(N + 1));
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
dp[0][0] = 1;
for (int i = 1; i <= n; i++)
{
for (int j = 0; j <= N; j++)
{
dp[i & 1][j] = dp[(i - 1) & 1][j];
if ((j ^ a[i]) <= N)
{
dp[i & 1][j] = (dp[i & 1][j] + dp[(i - 1) & 1][j ^ a[i]]) % mod;
}
}
}
int res = 0;
for (int i = 1; i <= N; i++)
{
if (!st[i])
res = (res + dp[n & 1][i]) % mod;
}
cout << res << endl;
}