如果不考虑 \(A_i\neq A_j\) 的条件非常好做,不难想到将这个条件容斥掉。
这等价于从 \(\dfrac{N(N - 1)}{2}\) 个二元组中钦定一些使得对应两端相同,表现在图中就是求连通块个数和对应的 \(\rm lcm\)。
所以我们不难设计 DP,\(f_{S}\) 表示集合为 \(S\) 的所有点的答案,我们枚举包含集合中最小点的连通块 \(T\),\(f_{S} = \sum\limits_{T}f_{S/T}g_{T}\)。
其中 \(g_{S}\) 表示集合为 \(S\) 的连通块容斥后的答案。由于联通块中所有数相同,我们求出它们的 \(\rm lcm\),\(m/\rm lcm\) 就是填数的方案。
但是我们还要计算容斥系数,选了奇数条边的系数为 \(-1\),偶数条边的系数为 \(1\)。
如果不要求连通且点数 \(\ge 2\),那么选奇数条边和偶数条边方案相同,总和为 \(0\)。
考虑枚举点 \(1\) 所在集合 \(T\),如果 \(|S/T|\ge 2\),那么这 \(\ge 2\) 个点选奇数条边和偶数条边方案相同,正负相消不用考虑。
再考虑求 \(|S/T| = 1\) 的情况,其中 \(T\) 必须包含点 \(1\),所以方案数有 \(|S| - 1\)。
如果我们记点集大小为 \(n\) 的容斥系数为 \(h_n\),则有 \(h_{n} = -(n - 1) h_{n - 1}\),预处理一下即可。
时间复杂度 \(\mathcal{O}(3^n)\),瓶颈在于 DP。
#define N 17
int n;LL m, g[1 << N], bt[1 << N], f[1 << N], h[N], d[N];
int main() {
read(n), read(m);
rp(i, n)read(d[i]);
g[0] = 1; int w = 1 << n;
rp(i, w - 1){
bt[i] = bt[i >> 1] + (i & 1);
rep(j, 0, n - 1)if((i >> j) & 1){g[i] = lcm(g[i ^ (1 << j)], d[j + 1]);break;}
}
rp(i, w - 1)g[i] = (m / g[i]) % P;
h[1] = 1;
rep(i, 2, n)h[i] = (P - i + 1LL) * h[i - 1] % P;
f[0] = 1;
rp(i, w - 1){
for(int j = i; j; j = (j - 1) & i)if(j & (i & -i))
ad(f[i], f[i ^ j] * 1LL * g[j] % P * h[bt[j]] % P);
}
cout << f[w - 1] << endl;
return 0;
}