【LOJ2463】「2018 集训队互测 Day 1」完美的旅行

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39972971/article/details/88650110

【题目链接】

【思路要点】

  • 考虑计算总共走了 x x 步,且所有旅行的愉悦值的按位与的结果包含 y y 的方案数,再简单容斥得到答案。
  • 记一次旅行走了 i   ( i 1 ) i\ (i\geq 1) 步,且其愉悦值包含 y y 的方案数为 a i a_i
  • f ( x ) = a i x i f(x)=\sum a_ix^i ,那么进行若干次旅行总共走了 i i 步且所有旅行的愉悦值的按位与的结果包含 y y 的方案数即为
    ( 1 + f ( x ) + f 2 ( x ) + f 3 ( x ) + . . . ) [ i ] = 1 1 f ( x ) [ i ] (1+f(x)+f^2(x)+f^3(x)+...)[i]=\frac{1}{1-f(x)}[i]
  • 剩余的问题在于计算 a i a_i ,注意到若记 w a y s i ( x , y ) ways_{i}(x,y) 表示由 x x y y 共走 i i 步的方案数, a i a_i 是一系列 w a y s i ( x , y ) ways_i(x,y) 的和,而 w a y s i ( x , ) ways_{i}(x,*) 的转移方式即为乘上给定的边数矩阵。
  • C a y l e y H a m i l t o n Cayley-Hamilton 定理,其特征多项式为化零多项式,因此 w a y s i ( x , y ) ways_{i}(x,y) 对于固定的 x , y x,y 存在一个 O ( N ) O(N) 阶的线性递推。
  • 暴力计算 a i a_i 的前 O ( N ) O(N) 项,用 B e r l e k a m p M a s s e y Berlekamp-Massey 算法 计算其递推式,直接递推即可计算 a i a_i
  • 时间复杂度 O ( N 4 + N 2 M + N M L o g M ) O(N^4+N^2M+NMLogM)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 64;
const int MAXM = 65536;
const int P = 998244353;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> 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 * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
namespace BerlekampMassey {
	const int MAXN = 205;
	const int P = 998244353;
	int cnt, fail[MAXN], delta[MAXN];
	vector <int> ans[MAXN];
	int power(int x, int y) {
		if (y == 0) return 1;
		int tmp = power(x, y / 2);
		if (y % 2 == 0) return 1ll * tmp * tmp % P;
		else return 1ll * tmp * tmp % P * x % P;
	}
	vector <int> work(int n, int *val) {
		ans[cnt = 0].clear();
		for (int i = 1; i <= n; i++) {
			int now = val[i];
			for (unsigned j = 0; j < ans[cnt].size(); j++)
				now = (now - 1ll * val[i - j - 1] * ans[cnt][j] % P + P) % P;
			delta[i] = now; if (now == 0) continue;
			fail[cnt] = i;
			if (cnt == 0) {
				ans[++cnt].clear();
				ans[cnt].resize(i);
				continue;
			}
			ans[++cnt].clear();
			ans[cnt].resize(i - fail[cnt - 2] - 1);
			int mul = 1ll * now * power(delta[fail[cnt - 2]], P - 2) % P;
			ans[cnt].push_back(mul);
			for (unsigned j = 0; j < ans[cnt - 2].size(); j++)
				ans[cnt].push_back(1ll * ans[cnt - 2][j] * (P - mul) % P);
			if (ans[cnt].size() < ans[cnt - 1].size()) ans[cnt].resize(ans[cnt - 1].size());
			for (unsigned j = 0; j < ans[cnt - 1].size(); j++)
				ans[cnt][j] = (ans[cnt][j] + ans[cnt - 1][j]) % P;
		}
		//cerr << ans[cnt].size() << endl;
		return ans[cnt];
	}
}
namespace NTT {
	const int MAXN = 65536;
	const int P = 998244353;
	const int G = 3;
	int power(int x, int y) {
		if (y == 0) return 1;
		int tmp = power(x, y / 2);
		if (y % 2 == 0) return 1ll * tmp * tmp % P;
		else return 1ll * tmp * tmp % P * x % P;
	}
	int N, Log, home[MAXN];
	void NTTinit() {
		for (int i = 0; i < N; i++) {
			int ans = 0, tmp = i;
			for (int j = 1; j <= Log; j++) {
				ans <<= 1;
				ans += tmp & 1;
				tmp >>= 1;
			}
			home[i] = ans;
		}
	}
	void NTT(int *a, int mode) {
		for (int i = 0; i < N; i++)
			if (home[i] < i) swap(a[i], a[home[i]]);
		for (int len = 2; len <= N; len <<= 1) {
			int delta;
			if (mode == 1) delta = power(G, (P - 1) / len);
			else delta = power(G, P - 1 - (P - 1) / len);
			for (int i = 0; i < N; i += len) {
				int now = 1;
				for (int j = i, k = i + len / 2; k < i + len; j++, k++) {
					int tmp = a[j];
					int tnp = 1ll * a[k] * now % P;
					a[j] = (tmp + tnp) % P;
					a[k] = (tmp - tnp + P) % P;
					now = 1ll * now * delta % P;
				}
			}
		}
		if (mode == -1) {
			int inv = power(N, P - 2);
			for (int i = 0; i < N; i++)
				a[i] = 1ll * a[i] * inv % P;
		}
	}
	void times(int *a, int *b, int *c, int limit) {
		N = 1, Log = 0;
		while (N < 2 * limit) {
			N <<= 1;
			Log++;
		}
		for (int i = limit; i < N; i++)
			a[i] = b[i] = 0;
		NTTinit();
		NTT(a, 1);
		NTT(b, 1);
		for (int i = 0; i < N; i++)
			c[i] = 1ll * a[i] * b[i] % P;
		NTT(c, -1);
	}
	void timesabb(int *a, int *b, int *c, int limit) {
		N = 1, Log = 0;
		while (N < 2 * limit) {
			N <<= 1;
			Log++;
		}
		for (int i = limit; i < N; i++)
			a[i] = 0;
		for (int i = limit / 2; i < N; i++)
			b[i] = 0;
		NTTinit();
		NTT(a, 1);
		NTT(b, 1);
		for (int i = 0; i < N; i++)
			c[i] = 1ll * a[i] * b[i] % P * b[i] % P;
		NTT(c, -1);
	}
	void inverse(int *a, int *b, int limit) {
		for (int i = 0; i < 2 * limit; i++) {
			if (i >= limit) a[i] = 0;
			b[i] = 0;
		}
		b[0] = power(a[0], P - 2);
		for (int now = 1; now < limit; now <<= 1) {
			static int c[MAXN], d[MAXN];
			for (int i = 0; i < now * 2; i++)
				c[i] = a[i], d[i] = b[i];
			timesabb(c, d, d, now * 2);
			for (int i = 0; i < now * 2; i++)
				b[i] = (2ll * b[i] - d[i] + P) % P;
		}
	}
}
int n, m;
int mat[MAXN][MAXN];
int res[MAXN][MAXM];
int inv[MAXN][MAXM];
void update(int &x, int y) {
	x += y;
	if (x >= P) x -= P;
}
int main() {
	read(n), read(m);
	for (int i = 0; i < n; i++)
	for (int j = 0; j < n; j++)
		read(mat[i][j]);
	static int ways[MAXN * 2 + 15][MAXN][MAXN];
	for (int i = 0; i < n; i++)
		ways[0][i][i] = 1;
	int steps = n * 2 + 5;
	for (int t = 1; t <= steps; t++) {
		for (int i = 0; i < n; i++)
		for (int j = 0; j < n; j++)
		for (int k = 0; k < n; k++)
			update(ways[t][i][k], 1ll * ways[t - 1][i][j] * mat[j][k] % P);
		for (int i = 0; i < n; i++)
		for (int j = 0; j < n; j++)
			update(res[i & j][t], ways[t][i][j]);
	}
	static int tmp[MAXN * 2 + 15];
	for (int t = 1; t <= steps; t++) {
		int tres = 0;
		for (int i = 0; i < n; i++)
			tres = (13331ll * tres + res[i][t]) % P;
		tmp[t] = tres;
	}
	vector <int> h = BerlekampMassey :: work(steps, tmp);
	for (int i = 0; i < n; i++) {
		for (int t = steps + 1; t <= m; t++) {
			int tres = 0;
			for (unsigned j = 0; j < h.size(); j++)
				update(tres, 1ll * res[i][t - j - 1] * h[j] % P);
			res[i][t] = tres;
		}
	}
	for (int i = 0; i < n; i++) {
		for (int t = 1; t <= m; t++)
		for (int j = i + 1; j < n; j++)
			if ((i | j) == j) update(res[i][t], res[j][t]);
		res[i][0] = 1;
		for (int j = 1; j <= m; j++)
			res[i][j] = P - res[i][j];
		NTT :: inverse(res[i], inv[i], m + 1);
	}
	int ans = 0;
	for (int t = 1; t <= m; t++) {
		for (int i = n - 1; i >= 0; i--) {
			for (int j = i + 1; j < n; j++)
				if ((i | j) == j) update(inv[i][t], P - inv[j][t]);
			ans ^= inv[i][t];
		}
	}
	writeln(ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39972971/article/details/88650110