C - Fluorescent 2

首先将三次方转化一下,设进行操作后,第 个位置的开关情况为 表示关, 表示开),那么三次方就变成了:

再转化一下:

对三项分别计算即可。

先计算第一项,枚举每一项 ,发现这时只和字符串第 列有关,如果该列全为 ,则方案数为

接下来考虑存在至少一个 的情况,可以先选一个 ,设其在第 行,那么另外 个位置任意选或不选,选择完毕后如果开关开了,那么就可以不选第 行,反之就选,所以方案数为

接下来计算第二项,这里可以枚举 ,接下来为了方便,设第 列形成的二进制数为 ,并且由于后面的方案数计算和计算第一项时类似,就不展开了,只给结论。

  • ,方案数为 .
  • ,方案数为 .
  • ,方案数为 .
  • ,方案数为 .

然后是第三项,继续分类讨论,实现(计算每种情况的个数)见代码:

  • .
  • .
  • .
  • .
  • .
  • 互不相等,都不为 .
  • .
  • 互不相等,都不为 .
int n, m; char s[N][M]; ll bit[M]; bool F[M];
map<ll, int> mp; mint pw[N], C[M][M];
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	int lim = 51;
	pw[0] = 1;
	forn (i, 1, lim) pw[i] = pw[i - 1] * mint(2);
	lim = 1000;
	C[0][0] = 1;
	forn (i, 1, lim) {
		C[i][0] = 1;
		forn (j, 1, lim) C[i][j] = C[i - 1][j] + C[i - 1][j - 1];
	}
	while (cin >> n >> m) {
		mp.clear();
		rep (i, 0, n) cin >> s[i];
		rep (j, 0, m) {
			bit[j] = 0;
			rep (i, 0, n) bit[j] = bit[j] << 1 | ((int)s[i][j] - int('0'));
			if (mp.count(bit[j])) F[j] = 0;
			else F[j] = 1;
			mp[bit[j]] ++ ;
		}
		mint ans = 0;
		// case 1 单个数字
		rep (i, 0, m) 
			if (bit[i]) ans += pw[n - 1];
			else ans += pw[n];
		// case 2 两个数字
		rep (i, 0, m) rep (j, i + 1, m) 
			if (!bit[i] && !bit[j]) 
				ans += mint(6) * pw[n];
			else if (!bit[i] || !bit[j] || (bit[i] == bit[j])) 
				ans += mint(6) * pw[n - 1];
			else 
				ans += mint(n >= 2) * mint(6) * pw[n - 2];
		// case 3 三个数字
		mint cnt = C[m][3]; int n0 = 0;
		// case 3.1 bit[i] = bit[j] = bit[k] = 0
		if (mp.count(0ll)) {
			n0 = mp[0];
			cnt -= C[n0][3];
			ans += mint(6) * C[n0][3] * pw[n];
		}
		// case 3.2 bit[i] != 0, bit[j] = bit[k] = 0
		rep (i, 0, m) if (bit[i] && F[i]) //#
			ans += mint(6) * C[n0][2] * mint(mp[bit[i]]) * pw[n - 1],
			cnt -= C[n0][2] * mint(mp[bit[i]]);
		// case 3.3 bit[i] != 0, bit[j] != 0, bit[k] = 0 \and bit[i] = bit[j]
		rep (i, 0, m) if (bit[i] && F[i]) //#
			ans += mint(6) * C[mp[bit[i]]][2] * mint(n0) * pw[n - 1],
			cnt -= C[mp[bit[i]]][2] * mint(n0);
		// case 3.4 bit[i] != 0, bit[j] != 0, bit[k] = 0 \and bit[i] != bit[j]
		rep (i, 0, m) rep (j, i + 1, m) //#
			if (bit[i] && bit[j] && bit[i] != bit[j])
				ans += mint(n >= 2) * mint(6) * mint(n0) * pw[n - 2],
				cnt -= mint(n0);
		// case 3.5 bit[i] = bit[j] = bit[k] != 0;
		rep (i, 0, m) if (bit[i] && F[i]) //#
			ans += mint(6) * C[mp[bit[i]]][3] * pw[n - 1],
			cnt -= C[mp[bit[i]]][3];
		// case 3.6 bit[i], bit[j], bit[k] 互不相等,都不为零 bit[i] ^ bit[j] == bit[k]
		int cur = 0;
		rep (i, 0, m) rep (j, i + 1, m) // # 
			if (bit[i] && bit[j] && mp.count(bit[i] ^ bit[j]) && (bit[i] ^ bit[j]))
				ans += mint(2) * mint(n >= 2) * mp[bit[i] ^ bit[j]] * pw[n - 2], 
				cur += mp[bit[i] ^ bit[j]];
		cur /= 3, cnt -= mint(cur);
		// case 3.7 bit[i] = bit[j] != 0, bit[k] != 0
		rep (i, 0, m) if (bit[i] && F[i])
			ans += mint(6) * mint(n >= 2) * C[mp[bit[i]]][2] * mint(m - n0 - mp[bit[i]]) * pw[n - 2],
			cnt -= C[mp[bit[i]]][2] * mint(m - n0 - mp[bit[i]]);
		// case 3.8 bit[i], bit[j], bit[k] 互不相等,都不为零 bit[i] ^ bit[j] != bit[k]
		if (n >= 3) ans += mint(6) * cnt * pw[n - 3];
		cout << ans.r << '\n';
	}
	return 0;
}