【NOI Online #3 提高组】魔法值

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(qn231)=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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值