洛谷 P6569 [NOI Online #3 提高组] 魔法值 题解

魔法值

link

Description

给定一张有 \(n\) 个点和 \(m\) 条边的无向图。在时刻 \(j\) 时,\(i\) 号节点的权值为 \(f_{i,j}\)

已知 \(\forall i \in [1,n],\ f_{i,0}\) 的值。对于每个新的时刻 \(j\)\(f\) 满足如下变换:

\[f_{u,j} = \bigoplus_{v} f_{v,j-1} \]

其中 \(v\) 为与 \(u\) 直接相连的节点。

给定 \(q\) 次询问,对于第 \(i\) 次询问,需要回答时刻 \(a_i\) 时,\(1\) 号节点的权值是多少。

对于 \(100\%\) 的数据,满足 \(1 \leq n,q \leq 100\)\(1 \leq m \leq \frac{n(n-1)}{2}\)\(1\leq a_i < 2^{32}\)\(0\leq f_{i,0} < 2^{32}\)

Solution

注意到图并没有什么用,有用的只是点与点间的直接连通性。

\(e_{i,j}\) 表示 \(i\)\(j\) 是否有直接连边,则有

\[f_{u,j} = \bigoplus_{v = 1}^{n} f_{v,j - 1} \times e_{i,j} \]

不妨把 \(f_{u,j}\) 抽象为一维矩阵 \(F_i\),并设邻接矩阵为 \(E\),那么则有

\[F_i = F_{i-1} \star E \]

其中,\(\star\)\((\times,\oplus)\) 运算。

容易发现,如果 \(\star\) 运算满足结合律,那么必有

\[F_i = F_0 \star E^i \]

其中幂运算是在 \(\star\) 意义下的。

\(\star\) 满足结合律的证明:

\[\begin{aligned} ((A \star B) \star C)_{ij} &= \bigoplus_{k} \left( (A \star B)_{ik} \times C_{kj} \right) \\ &= \bigoplus_{k} \left( \left( \bigoplus_{l} (A_{il} \times B_{lk}) \right) \times C_{kj} \right) \\ &= \bigoplus_{k} \bigoplus_{l} \left( (A_{il} \times B_{lk}) \times C_{kj} \right) \\ &= \bigoplus_{l} \bigoplus_{k} \left( A_{il} \times (B_{lk} \times C_{kj}) \right) \\ &= \bigoplus_{l} \left( A_{il} \times \left( \bigoplus_{k} (B_{lk} \times C_{kj}) \right) \right) \\ &= \bigoplus_{l} \left( A_{il} \times (B \star C)_{lj} \right) = (A \star (B \star C))_{ij}. \end{aligned} \]

对于每一次询问,处理出 \(E\) 的正整数次幂即可。每次都处理显然过于麻烦,因此考虑倍增:对于每一个 \(j\),预处理出矩阵 \(E\)\(2^j\) 次幂。累计答案时拆位即可。

Code

#include <bits/stdc++.h>
#define int long long
#define inf 1e18
#define debug cout << '!';
#define filein(x) freopen(#x".in", "r", stdin);
#define fileout(x) freopen(#x".out", "w", stdout);
#define file(x) filein(x) fileout(x)
// #define Fast_IO
using namespace std;
#ifdef Fast_IO
inline int read() {
	int x = 0, f = 1; char c = getchar();
	while (c < '0' or c > '9') { if (c == '-') f = -1; c = getchar(); }
	while (c >= '0' and c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
	return x * f;
}
void write(int x) {
	if (x < 0) putchar('-'), x = -x;
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0'); return;
}
#endif
const int MaxN = 105, MaxM = 40;
int n, m, q;
struct Mat {
	int row, col;
	int mt[MaxN][MaxN];
	Mat() { memset(mt, 0, sizeof(mt)); }
	friend Mat operator * (Mat x, Mat y) {
		Mat res;
		res.row = x.row, res.col = y.col;
		for (int k = 1; k <= x.col; k++) {
			for (int j = 1; j <= y.col; j++) {
				for (int i = 1; i <= x.row; i++) {
					res.mt[i][j] ^= x.mt[i][k] * y.mt[k][j];
				}
			}
		}
		return res;
	}
} I, E[MaxM];
int f[MaxN];
Mat qpow(Mat x, int p) {
	if (p == 0) return I;
	if (p == 1) return x;
	Mat tmp = qpow(x * x, p / 2);
	if (p & 1) return tmp * x;
	return tmp;
}
signed main() {
	cin.tie(0) -> sync_with_stdio(0);
	cin >> n >> m >> q;
	for (int i = 1; i <= n; i++) {
		cin >> f[i];
	}
	E[0].row = E[0].col = n;
	for (int i = 1; i <= m; i++) {
		int u, v; cin >> u >> v;
		E[0].mt[u][v] = E[0].mt[v][u] = 1;
	}
	for (int i = 1; i < 32; i++) {
		E[i] = E[i - 1] * E[i - 1];
	}
	for (; q--; ) {
		int x; cin >> x;
		Mat ans;
		for (int i = 1; i <= n; i++) {
			ans.mt[1][i] = f[i];
		}
		ans.row = 1, ans.col = n;
		for (int i = 0; i < 32; i++) {
			if ((x >> i) & 1) ans = ans * E[i];
		}
		cout << ans.mt[1][1] << '\n';
	}
	return 0;
}
posted @ 2026-05-15 10:40  L-Coding  阅读(3)  评论(0)    收藏  举报