P6570 [NOI Online #3 提高組]優秀子序列
阿新 • • 發佈:2020-10-29
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 \bigoplus j}\) 其中 \(j\) 為 \(i\) 的子集, \(i \bigoplus j\) 為 \(j\) 的補集。
可以發現 \(f_{i} \cdot t_{i \bigoplus j}\)
那麼我們只考慮 \(i \lt i \bigoplus 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\n", ans % MOD); return 0; }