一个渐进意义下 的做法。
代表集合
。
问题转化
考虑一个平凡的转化:考虑去计算所有不合法的方案数。
一个方案不合法当且仅当存在四元组中存在两个位置上的元素相等。
令 为所有位置
上的元素和位置
上的元素相等的四元组构成的集合。
那么我们只需要求出以下式子的值即可:
固定
的分类讨论
考虑固定 。
的情况:
因为容斥系数为 所以直接枚举
的所有大小为
的子集即可。
的情况:
考虑 , 按
分类讨论。
若 ,如果不考虑容斥系数直接枚举
的所有大小为
的子集即可。
考虑以下这里的容斥系数:对于任意一种 的大小为
的子集,考虑固定一个大小为
的子集,共
种方法,再钦定一个
共
种方法,因此容斥系数为
。
若 ,那么一定有
。
那么直接枚举 大小为
的子集再去处理子集的方案数相乘即可。
的情况:
考虑 ,按
分类讨论。
若 ,考虑其容斥系数,对于
的任意一种个大小为
的子集,其一定唯一对应一组唯一的
,因此容斥系数即
,同理枚举子集乘上即可。
若 ,其容斥系数的计算也是平凡的
,简单处理即可。
的情况:
对于 ,其集族内包括的集合的指标并的大小一定是
,因此其容斥系数一定是组合数,简单处理即可。
代码
#include <bits/stdc++.h> #define int long long int c[4][5005],n,s[4]; inline int count(int x){ int res = 0; for (;x;x>>=1) if (x&1) res++; return res; } inline int get(int state){ int res = 0; for (int i = 1; i <= n; i++){ bool ck = true; for (int j = 0; j < 4; j++) if (state >> j & 1 && c[j][i] == 0) ck = false; if (!ck) continue; res++; } return res; } inline int solve(int state){ int res = 1; res *= get(state); for (int i = 0; i < 4; i++){ if (state >> i & 1) continue; res *= s[i]; } return res; } signed main(){ std::cin >> n; for (int i = 0; i < 4; i++){ char str[5005]; std::cin >> str; for (int j = 0; j < n; j++) c[i][j+1] = str[j] == '1' ? 1 : 0; } for (int i = 0; i < 4; i++) for (int j = 1; j <= n; j++) s[i] += c[i][j]; int res = 0; int aa = 1; for (int i = 0; i < 4; i++) aa *= s[i]; res += aa; int s1 = 0; for (int i = 0; i < 1 << 4; i++){ if (count(i) != 2) continue; res -= solve(i); } for (int i = 0; i < 1 << 4; i++){ if (count(i) != 3) continue; res += solve(i) * 2; } int sum = 0; for (int i = 0; i < 1 << 4; i++){ if (count(i) != 2) continue; int newstate = 0; for (int j = 0; j < 4; j++) if (!(i >> j & 1)) newstate += 1 << j; sum += get(newstate) * get(i); } res += sum / 2; int ss = 0,s2 = 0; for (int i = 0; i < 1 << 4; i++) ss += 1 << i; res -= 6 * solve(ss); std::cout << res; return 0; }