\(\mathbb{Description}\)

\(\rm Link.\)

\(\mathbb{Solution}\)

然后你就点开了吗😆? 我是真没想到可以用 $Floyd$ (好吧其实也不是正宗的吧),感觉这个算法已经被我遗忘了 $QwQ$。(~~结果考场上把快速幂又打爆了~~)

定义 \(to[i][j][k]\)\(i\)\(j\) 的路径长度为 \(2^k\) 的方案总数,\(dp[i][j]\) 为从 \(1\)\(i\) 经过了 \(k\) 段的方案数。

我们将 \(a_i\) 分解成 \(2\) 的次方的和,这就是之前说的“段”。

最后我们知道从 \(1\)\(i\) 经过了 \(k\) 段相当于从 \(i\)\(1\) 经过了 \(k\) 段。考虑求魔法值的过程相当于一个从下到上不断异或的过程,我们只用将起点的初始魔法值异或起来即可。需要注意的是,如果一个点有偶数种方法到达 \(1\) 点,那么自己的魔法值就会抵消,所以要么异或要么不异或即可。

复杂度 \(O(q*n^2*31)=O(31000000)\)

还是把之前的内容留在这里吧……表述很烂,而且还有问题,所以不要点开😀。

\(\text{Method 1}\):直接 \(\mathtt{dp}\)

可以考虑每个城市对首都魔法值的贡献:计算出城市 \(i\) 花费 \(t\) 天到达首都的方案数(魔法值的传播可以看作点的移动),只有方案数为奇时才能对 \(\rm ans\) 有异或 \(f_i\) 的贡献。但每次计算就太慢了,我们考虑倍增,令 \(\text{to}_{i,j,k}\) 为从 \(i\)\(j\) 花费 \(2^k\) 的方案数,那么之后单次 \(\mathtt{dp}\) 就降到 \(\mathcal O(n^2\log a)\) 的复杂度,总共 \(\mathcal O(n^3\log a)\),可以通过此题。

\(\text{Method 2}\):矩阵乘法

朴素快速幂就懒得说了,这题学到的东西是转移矩阵可以预处理。复杂度和上面是一样的,但是我懒,所以实现非常粗糙。真的好水啊雾

另外还有一个矩阵需要注意的点:无论在区间合并中(比如线段树维护矩阵),还是在倍增实现转移矩阵时,都需要满足分配律,比方后者,实际上就是证明

\[\bigoplus_{x=1}^n\left (\bigoplus_{y=1}^n a_{i,y}\cdot b_{y,x}\right)\cdot c_{x,j}\Leftrightarrow \bigoplus_{x=1}^n \bigoplus_{y=1}^n a_{i,y}\cdot b_{y,x}\cdot c_{x,j}\Leftrightarrow \bigoplus_{y=1}^n a_{i,y}\cdot \left (\bigoplus_{x=1}^nb_{y,x}\cdot c_{x,j}\right) \]

可以发现,这实际上就是证明 \((a\oplus b)\cdot c=ac\oplus bc\).

\(\mathbb{Code}\)

\(\text{Method 1}\)

#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;
}

\(\text{Method 2}\)

#include <cstdio>
#define print(x,y) write(x),putchar(y)

template <class T>
inline T read(const T sample) {
	T x=0; char s; bool f=0;
	while((s=getchar())>'9' || s<'0')
		f |= (s=='-');
	while(s>='0' && s<='9')
		x = (x<<1)+(x<<3)+(s^48),
		s = getchar();
	return f?-x:x;
}

template <class T>
inline void write(T x) {
	static int writ[50],tp=0;
	if(x<0) putchar('-'),x=-x;
	do writ[++tp] = x-x/10*10, x/=10; while(x);
	while(tp) putchar(writ[tp--]^48);
}

# include <cstring>

typedef long long ll;

const int maxn = 105;

int n,m,q;
ll f[maxn];
struct mat {

	ll a[maxn][maxn];
	mat() { memset(a,0,sizeof a); }

	mat operator * (const mat& t) const {
		mat r;
		for(int i=0;i<n;++i) for(int j=0;j<n;++j)
			if(a[i][j]) for(int k=0;k<n;++k)
				r.a[i][k] ^= a[i][j]*t.a[j][k];
		return r;
	}

} tra[32],ans;

int main() {
	n=read(9), m=read(9), q=read(9);
	for(int i=1;i<=n;++i) f[i]=read(9ll);
	for(int i=1;i<=m;++i) {
		int u=read(9)-1, v=read(9)-1;
		tra[0].a[u][v] = tra[0].a[v][u] = 1;
	}
	for(int i=1;i<=31;++i) tra[i] = tra[i-1]*tra[i-1];
	while(q --) {
		ll s=read(9ll);
		for(int i=0;i<n;++i) ans.a[0][i]=f[i+1];
		for(int i=31; ~i; --i) 
			if(s>>i&1) ans = ans*tra[i];
		print(ans.a[0][0],'\n');
	}
	return 0;
}
posted on 2020-05-29 11:11  Oxide  阅读(130)  评论(0)    收藏  举报