Solution
我是真没想到可以用
F
l
o
y
d
Floyd
Floyd (好吧其实也不是正宗的吧),感觉这个算法已经被我遗忘了
Q
w
Q
QwQ
QwQ。(结果考场上把快速幂又打爆了)
定义 t o [ i ] [ j ] [ k ] to[i][j][k] to[i][j][k] 为 i i i 到 j j j 的路径长度为 2 k 2^k 2k 的方案总数, d p [ i ] [ j ] dp[i][j] dp[i][j] 为从 1 1 1 到 i i i 经过了 k k k 段的方案数。
我们将 a i a_i ai 分解成 2 2 2 的次方的和,这就是之前说的“段”。
最后我们知道从 1 1 1 到 i i i 经过了 k k k 段相当于从 i i i 到 1 1 1 经过了 k k k 段。考虑求魔法值的过程相当于一个从下到上不断异或的过程,我们只用将起点的初始魔法值异或起来即可。需要注意的是,如果一个点有偶数种方法到达 1 1 1 点,那么自己的魔法值就会抵消,所以要么异或要么不异或即可。
复杂度 O ( q ∗ n 2 ∗ 31 ) = O ( 31000000 ) O(q*n^2*31)=O(31000000) O(q∗n2∗31)=O(31000000)。
Code
#include <cstdio>
#include <cstring>
typedef long long ll;
const int N = 105;
int n, m, q, cnt, to[N][N][36], dp[N][36], s[36];
ll ans, f[N];
ll read() {
ll x = 0, f = 1; char s;
while((s = getchar()) < '0' || s > '9') if(s == '-') f = -1;
while(s >= '0' && s <= '9') {x = (x << 1) + (x << 3) + (s ^ 48); s = getchar();}
return x * f;
}
int main() {
ll u, v;
n = read(), m = read(), q = read();
for(int i = 1; i <= n; ++ i) f[i] = read();
for(int i = 1; i <= m; ++ i) {
u = read(), v = read();
to[u][v][0] = to[v][u][0] = 1;
}
for(int p = 1; p <= 31; ++ p)
for(int i = 1; i <= n; ++ i)
for(int j = 1; j <= n; ++ j)
for(int k = 1; k <= n; ++ k)
to[i][j][p] += to[i][k][p - 1] * to[k][j][p - 1];
while(q --) {
u = read(); cnt = 0; ans = 0; memset(dp, 0, sizeof dp);
for(int i = 31; ~i; -- i)
if((1ll << i) <= u) u -= (1ll << i), s[++ cnt] = i;
dp[1][0] = 1;//只在 1 号点设置值就好啦
for(int k = 1; k <= cnt; ++ k)
for(int i = 1; i <= n; ++ i)
for(int j = 1; j <= n; ++ j)
dp[i][k] += dp[j][k - 1] * to[j][i][s[k]];
for(int i = 1; i <= n; ++ i) {
dp[i][cnt] &= 1;
if(dp[i][cnt]) ans ^= f[i];
}
printf("%lld\n", ans);
}
return 0;
}