容斥大法妙~其实网上不少的题解虽然给出了容斥系数,可是并无说明为何是这个样子的。在这里解释一下好了。c++
考虑用容斥,实际上就是让 \(ans = \sum_{T\subseteq S}^{\ }f_{T}*h_{T}\)。其中,\(f\) 为容斥的系数,而 \(h\) 为一个集合的‘贡献’。这个贡献的值每每对于集合当中的各个元素而言是独立的。因为这题中是要咱们求出全部的被操做了奇数次的灯的数量,因此有:spa
\(g_{x}=\sum_{i = 1}^{x}\binom{x}{i}*f_{i}=[x\&1]\)code
\(g_{x}\) 为是原数列中 \(x\) 个数的倍数的数所对答案产生的贡献blog
令\(f[0] = 0\),get
则\(g_{x}=\sum_{i = 0}^{x}\binom{x}{i}*f_{i}=[x\&1]\)it
那么根据二项式反演,有class
\(f_x = \sum_{i = 0}^{x} g_i * \binom{x}{i}*(-1)^{x - i}\)gc
\(f_x = \sum_{i = 0}^{x}\binom{x}{i}*(-1)^{x - i}[x\&1]\)集合
根据\(f_x = \sum_{i = 0}^{x}\binom{x}{i}*(-1)^{x - i}[x\&1]\)di
对\(x\) 的奇偶性分类讨论一下,再加上:
\(\binom{n}{1}+\binom{n}{3}+\binom{n}{5}...=2^{n - 1}\)
(这个式子就不用解释了吧……)
而后就获得了\(f_x\) 的表达式~
下面这份代码为 \(n^{2}\) 求出容斥系数,但实际上能够按照上文所说作到\(O(1)\)……
#include <bits/stdc++.h> using namespace std; #define maxn 1000 #define int long long int n, m, ans, cnt, S[maxn]; int f[maxn], a[maxn], C[maxn][maxn]; int read() { int x = 0, k = 1; char c; c = getchar(); while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); } while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * k; } void Pre() { for(int i = 0; i < 30; i ++) C[i][0] = 1; for(int i = 1; i < 30; i ++) for(int j = 1; j < 30; j ++) C[i][j] = C[i - 1][j - 1] + C[i - 1][j]; } int Get(int x) { int t = x & 1; for(int i = 1; i < x; i ++) t -= C[x][i] * f[i]; return t; } int gcd(int a, int b) { int c = 0; while(b) c = a % b, a = b, b = c; return a; } void dfs(int now) { if(now == m + 1) { int lcm = 1; for(int i = 1; i <= cnt; i ++) lcm = lcm * S[i] / gcd(lcm, S[i]); ans += f[cnt] * (n / lcm); return; } S[++ cnt] = a[now]; dfs(now + 1); cnt --; dfs(now + 1); } signed main() { int T = read(); f[0] = 0; f[1] = 1; Pre(); for(int i = 2; i <= 20; i ++) f[i] = Get(i); for(int i = 1; i <= T; i ++) { n = read(), m = read(); ans = 0; for(int j = 1; j <= m; j ++) a[j] = read(); dfs(1); printf("%lld\n", ans); } return 0; }