P6570 [NOI Online #3 提高组]优秀子序列
写的 (O(3^{log_2 max{a_i}})) 的做法,原因是不会题解里的自己子集卷积。
首先容易想到一个 (O (2^{log_n max { a_i }} n)) 的做法,不再赘述。
然后可以发现其实这玩意是位置无关的。
那我们直接开个桶,利用组合数和补集算一下发现 (f_{i} = f_{j} cdot t_{i igoplus j}) 其中 (j) 为 (i) 的子集, (i igoplus j) 为 (j) 的补集。
可以发现 (f_{i} cdot t_{i igoplus j}) 和 (f_{i igoplus j} cdot t_i) 是同一种情况。
那么我们只考虑 (i lt i igoplus j) 的情况。
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long ll;
const ll MAXN = 1e6+10, MOD = 1e9+7;
ll f[MAXN], N, B = 1, val[MAXN], cnt, prime[MAXN], vis[MAXN], phi[MAXN], ans, t[MAXN], maxn;
int main() {
scanf("%lld", &N);
phi[1] = 1, f[0] = 1;
for (ll i = 2; i <= MAXN - 10; i++) {
if (!vis[i]) prime[++cnt] = i, phi[i] = i-1;
for (ll j = 1; j <= cnt && prime[j] * i <= MAXN - 10; j++) {
vis[prime[j] * i] = 1;
if (i % prime[j]) {
phi[i * prime[j]] = phi[i] * phi[prime[j]];
} else {
phi[i * prime[j]] = phi[i] * prime[j];
break;
}
}
}
for (ll i = 1; i <= N; i++)
scanf("%lld", val+i), t[val[i]]++, maxn = max(maxn, val[i]);
while (maxn >= B) B *= 2;
for (ll i = 1; i <= B; i++) {
for (ll j = i;;j = (j - 1) & i) {
ll s = i ^ j;
if (j < s) break;
(f[i] += (f[s] * t[j] % MOD)) %= MOD;
}
}
ans = 1;
for (ll i = 1; i <= B; i++)
(ans += (phi[i+1] * f[i]) % MOD) %= MOD;
for (ll i = 1; i <= t[0]; i++)
ans = ans * 2 % MOD;
printf("%lld
", ans % MOD);
return 0;
}