[NOI Online #3 提高组] 优秀子序列
把 ai 拆成二进制,视作一个形如 {010010} 的集合,那就是找出所有的不相交的集合的无交并的权值之和
设 dpS 表示无交并为 S 的方案数,答案为 S∑dpSφ(S+1)
容易推出 dpS=S′∈S∑dpS′×cntS−S′,就是选出来子集然后补成集合 S
可以子集卷积,但没必要
好吧还是卷一下
设Fi(x)=xai+xϕ
卷积为 (F*G)[k]=\sum\limits_{i|j=k,i\\&j=0}F[i]G[j]
答案就是 ∏Fi(x)的系数
按照付公主背包那题套路一下变成
exp∑ln(xai+xϕ)=expi∑k∑k(−1)k+1(xai)k
显然 (xai)k当且仅当 k=1 时不为零(子集卷积意义下)
所以就是求 exp∑xai
O(n2) exp即可(没写)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| #include <iostream> using namespace std; const int N = 300005, mod = 1000000007;
int n, mmax, cnt; int a[N], b[N], dp[N]; int pcnt, primes[N], phi[N]; bool vis[N];
void pre(int n) { cnt = 0; while ((1 << cnt) <= mmax) { ++cnt; } pcnt = 0; phi[1] = 1; for (int i = 2; i <= n; ++i) { if (!vis[i]) { primes[++pcnt] = i; phi[i] = i - 1; } for (int j = 1; j <= pcnt && primes[j] * i <= n; ++j) { vis[primes[j] * i] = 1; if (i % primes[j] == 0) { phi[primes[j] * i] = phi[i] * primes[j]; break; } else { phi[primes[j] * i] = phi[i] * (primes[j] - 1); } } } }
int main() { cin >> n; for (int i = 0; i < n; ++i) { int x; cin >> x; ++a[x]; mmax = max(mmax, x); } pre(N - 1); dp[0] = 1, b[0] = 0; for (int i = 1; i < (1 << cnt); ++i) { b[i] = b[i >> 1] << 1 | 1; if (a[i]) { int m = b[i] ^ i; for (int S = m;; S = (S - 1) & m) { dp[S | i] = (dp[S | i] + 1ll * dp[S] * a[i]) % mod; if (!S) { break; } } } } int ans = 0; for (int i = 0; i < (1 << cnt); ++i) { ans = (ans + 1ll * dp[i] * phi[i + 1]) % mod; } for (int i = 0; i < a[0]; ++i) { ans = 2 * ans % mod; } cout << ans << endl; }
|