题意分析
给定 n n n 个点, m m m 条边的无向图,第 i i i 个点的点权是 f [ i ] f[i] f[i] ,每次的点权会变化成为与其相连的点的异或和,给你 q q q 个查询,每一个查询输出第 q q q 次变化后 1 1 1 号点的点权。
思路
好了,题意理解成这样,大概就可以想象出一种暴力做法,但是会超时(这个错解在官方题解中有提及),可以自行算出复杂度证伪
第三场NOI Online试题-魔法值
书归正传,这个题中所说的异或运算是什么样子的呢:
操作数 1 1 1 (Decimal) | 操作数 2 2 2 (Decmial) | 操作数 1 1 1 (Binary) | 操作数 2 2 2 (Binary) | 运算符 (Operator) | 结果 (Result) |
---|---|---|---|---|---|
1 | 1 | 1 | 1 | ^ | 0 (0) |
0 | 0 | 0 | 0 | ^ | 0 (0) |
1 | 0 | 1 | 0 | ^ | 1 (1) |
0 | 1 | 0 | 1 | ^ | 1 (1) |
1 | 2 | 01 | 10 | ^ | 11 (3) |
我们可以发现,两位都是 1 1 1 ,异或出来反倒为 0 0 0 , 我们可以把它当成不进位的加法使用。
回到这道题,我们结合样例分析:
邻接矩阵:
$\ $ | 1 | 2 | 3 |
---|---|---|---|
1 | 0 | 1 | 1 |
2 | 1 | 0 | 1 |
3 | 1 | 1 | 0 |
这道题就可以作为一个矩阵乘法 + 快速幂解决,不断对 2 2 2 取模,得到当时的矩阵里面二进制数值之中 1 1 1 的个数,最后在根据这个不断地去异或即可。
这里介绍一种东西叫做 bitset
,一个数只占一个 bit
的空间,只能存储 0/1,可以当作上面的二进制矩阵使用。(最关键的是它还支持所有C++自带的二进制运算!! 如 &
, |
, 还有我们用到的^
)
代码实现
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n, m, q;
int f[101];
struct node {
bitset <101> s[101];
} I, a;
inline node operator * (node x, node y) {
node ret;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
ret.s[i][j] = ((x.s[i] & y.s[j]).count() & 1);
}
}
return ret;
}
inline node ksm(node dishu, int zhishu) {
node ret = I;
while (zhishu) {
if (zhishu & 1) {
ret = ret * dishu;
}
dishu = dishu * dishu;
zhishu >>= 1;
}
return ret;
}
signed main() {
ios::sync_with_stdio(0);
cin >> n >> m >> q;
for (int i = 0; i <= n; ++i) { // 单位矩阵初始化
for (int j = 0; j <= n; ++j) {
I.s[i][j] = (i == j ? 1 : 0);
}
}
for (int i = 1; i <= n; ++i) {
cin >> f[i];
}
for (int i = 1; i <= m; ++i) {
int u, v;
cin >> u >> v;
a.s[u][v] = 1;
a.s[v][u] = 1;
}
for (int i = 1; i <= q; ++i) {
int ssr;
cin >> ssr;
node dd = ksm(a, ssr); // 求出当时的矩阵
int ans = 0;
for (int j = 1; j <= n; ++j) {
ans ^= f[j] * 1ll * dd.s[j][1]; // 求出异或值
}
cout << ans << endl;
}
}