The 2nd Universal Cup. Stage 5: Northern J Sets May Be Good

发布时间 2023-10-18 22:20:24作者: zhaohaikun

题解

我们考虑计算 \(\sum_{S\subseteq\{1,2,3,\cdots,n\}} (-1)^{cnt(S)}\),这里 \(cnt(S)\) 表示 \(S\) 集合的导出子图的边数。

我们记 \(x_i=[i\in S]\)

我们考虑删掉 \(n\) 号点。

注意到如果 \(x_i\) 的取值会影响 \(cnt(s)\) 的奇偶性,则正负相消,贡献为 \(0\)

所以我们需要保证下列条件满足:

\[\sum_{i=1}^{n-1}a_{n,i} x_i + a_{n,n}\equiv 0\pmod 2 \]

我们考虑是否存在位置 \(pos\),满足 \(a_{n,pos}(pos\ne n) =1\)

  1. 如果不存在这样的 \(pos\),那我们根据 \(a_{n,n}\) 的取值分类讨论:

    1. \(a_{n,n}=0\),则贡献乘 \(2\)
    2. \(a_{n,n}=1\),贡献变成 \(0\)
  2. 如果存在一个位置 \(pos\),满足 \(a_{n,pos}=1\)

    那么,根据 \(\sum_{i=1}^{n-1}a_{n,i} x_i + a_{n,n}\equiv 0\pmod 2\),我们可以得出:

\[ x_{pos}\equiv \sum_{i=1\land i \ne pos}^{n-1} a_{n,i} x_i + a_{n,n} \pmod 2 \]

​ 我们考虑对于一个序列 \(x_1,x_2,\cdots,x_n\) 计算答案。

\[\begin{aligned} &\sum_{i=1}^{n}\sum_{j=i+1}^n a_{i,j} x_i x_j\\ \equiv&\sum_{i=1}^{n-1}\sum_{j=i+1}^{n-1} a_{i,j} x_i x_j\\ \equiv&\sum_{i=1\land i \ne pos}^{n - 1}\sum_{j=i+1\land j\ne pos}^{n - 1} a_{i,j} x_ix_j+\sum_{i=1\land i \ne pos}^{n-1} a_{i,pos} x_ix_{pos} + a_{pos,pos}x_{pos}\\ \equiv&\sum_{i=1\land i \ne pos}^{n - 1}\sum_{j=i+1\land j\ne pos}^{n - 1} a_{i,j} x_i x_j+\sum_{i=1\land i \ne pos}^{n-1} a_{i,pos} x_i(\sum_{i=1\land i \ne pos}^{n-1} a_{n,i} x_i+a_{n,n})+a_{pos,pos}(\sum_{i=1\land i \ne pos}^{n-1} a_{n,i} x_i+a_{n,n}) \end{aligned} \]

​ 于是,我们就可以把 \(n\times n\) 的矩阵,在 \(O(\frac{n^2}{w})\) 的时间内,变成 \((n-2)\times (n-2)\) 的矩阵。

​ 注意到 \(x_n\) 有两种取值,所以贡献要 \(\times 2\)。注意到上述式子继续展开,则 \(a_{pos,pos}a_{n,n}\) 的项,所以贡献可能 \(\times (-1)\)

总时间复杂度 \(O(\frac{n^3}{w})\)

代码

// MagicDark
#include <bits/stdc++.h>
#define debug cerr << "[" << __LINE__ << "] "
#define SZ(x) (int) x.size() - 1
#define all(x) x.begin(), x.end()
#define ms(x, y) memset(x, y, sizeof x)
#define F(i, x, y) for (int i = (x); i <= (y); i++)
#define DF(i, x, y) for (int i = (x); i >= (y); i--)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
template <typename T> inline void chkmax(T& x, T y) { x = max(x, y); }
template <typename T> inline void chkmin(T& x, T y) { x = min(x, y); }
template <typename T> inline void read(T &x) {
	x = 0; int f = 1; char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
	x *= f;
}
const int N = 1010, MOD = 998244353, inv2 = (MOD + 1) >> 1;
int n, m, ans = 1, t = 1;
bitset <N> a[N];
signed main() {
	read(n), read(m);
	F(i, 1, n) t = 2 * t % MOD;
	F(i, 1, m) {
		int x, y; read(x), read(y);
		a[x][y] = a[y][x] = true;
	}
	DF(i, n, 1) {
		bool flag = false;
		F(j, 1, i - 1) flag |= a[i][j];
		if (flag) {
			int pos = -1;
			F(j, 1, i - 1)
				if (a[j][i]) {
					pos = j;
					break;
				}
			assert(~pos);
			a[i][pos] = false;
			F(j, 1, i - 1) if (j != pos && a[pos][j]) {
				a[j] ^= a[i];
				if (a[i][i] ^ a[i][j]) a[j][j].flip();
			}
			F(j, 1, i - 1) if (j != pos && a[i][j]) {
				a[j] ^= a[pos];
			}
			if (a[pos][pos]) {
				F(j, 1, i - 1)
					if (j != pos && a[i][j]) a[j][j].flip();
				if (a[i][i]) ans = (ll) ans * (MOD - 1) % MOD;
			}
			F(j, 1, i - 2) {
				if (j >= pos) a[j] = a[j + 1];
				bitset <N> s = (a[j] >> pos);
				a[j] = a[j] ^ (s << pos) ^ ((s >> 1) << pos);
			}
			i--;
			ans = (ll) ans * 2 % MOD;
		} else if (!a[i][i]) ans = (ll) ans * 2 % MOD;
			else ans = 0;
	}
	cout << (ll) (t + ans) * inv2 % MOD;
	return 0;
}
/* why?
*/