[NOI Online #3 提高组]魔法值

description

solution:

这道题关于矩阵。。原来根本不会啊啊
我们记(e_{i,j}=0/1)表示i到j是否有直接连边
由于原图是一个无向图,所以(e_{i,j}=e_{j,i})
那么原图中的等式
(f_{x,j}=f_{v_1,j-1}⊕f_{v_2,j-1}⊕...⊕f_{v_k,j-1})
就可以转化为
(f_{x,j}=e_{x,1}*f_{1,j-1}⊕e_{x,2}*f_{2,j-1}⊕...⊕e_{x,n}*f_{n,j-1})
也就是说

[left[ egin{matrix} e_{1,1} & e_{1,2} & cdots & e_{1,n} \ e_{2,1} & e_{2,2} & cdots & e_{2,n} \ vdots & vdots & ddots & vdots \ e_{n,1} & e_{n,2} & cdots & e_{n,n} \ end{matrix} ight] left[ egin{matrix} f_{1,j}\ f_{2,j}\ vdots &\ f_{n,j}\ end{matrix} ight]= left[ egin{matrix} f_{1,j+1}\ f_{2,j+1}\ vdots &\ f_{n,j+1}\ end{matrix} ight] ]

不过这里矩阵乘法的定义有些不同。
这里将矩阵乘法中的“加法”变为“异或”
于是乎第j天的(f)就可以通过矩阵快速幂做出来
但是这么做的前提条件就是这种“新式矩阵”像普通矩阵那样满足结合律
这是比较好证明的,直接可以根据(e)(0/1)分类讨论可以证明(同时这个01矩阵若干次幂后仍然是01矩阵,因为每次最后都是异或起来的)
具体来说,相当于要证明:(A*(B*C)=(A*B)*C)
发现如果证明(A*(B*C)=A*B*C=(A*B)*C)则原命题成立
考虑乘出来之后的矩阵:(M_{i,j}=XOR_{x=1}^nA_{i,x}*XOR_{y=1}^mB_{x,y}*C_{y,j})

是否等价于(M`_{i,j}=XOR_{x=1}^nXOR_{y=1}^mA_{i,x}*B_{x,y}*C_{y,j})

(A_{i,x}==0)时显然二者在x处是等价的
(A_{i,x}==1)时发现也是等价的

因此结论得证。另一边同理可证。因此这种新的矩阵乘法满足结合律。
然后考虑到有多次询问,我们可以预处理原矩阵的2的整数次幂
对于每次询问就可以直接(O(logn))次矩乘求出来

#include<bits/stdc++.h>
using namespace std;
typedef unsigned int uint;
const int N=105,LOG=32;
uint n,m,q;
struct Matrix
{
	uint c[N][N];
	inline void pre(){memset(c,0,sizeof(c));}
}M[LOG],f;
Matrix mul(const Matrix&x,const Matrix&y,uint a=n,uint b=n,uint c=n)
{
	Matrix z;
	for(uint i=1;i<=a;++i)
		for(uint j=1;j<=c;++j)
		{
			z.c[i][j]=0;
			for(uint k=1;k<=b;++k)
				z.c[i][j]^=x.c[i][k]*y.c[k][j];
		}
	return z;
}
int main()
{
	scanf("%u%u%u",&n,&m,&q);
	M[0].pre(),f.pre();
	for(uint i=1;i<=n;++i)scanf("%u",&f.c[i][1]);
	for(uint i=1;i<=m;++i)
	{
		uint x,y;scanf("%u%u",&x,&y);
		M[0].c[x][y]=M[0].c[y][x]=1;
	}
	for(uint i=1;i<LOG;++i)M[i]=mul(M[i-1],M[i-1]);
	while(q--)
	{
		uint a;scanf("%u",&a);
		Matrix ans=f;
		for(uint i=0;(1ll<<i)<=a;++i)
			if(a&(1ll<<i))ans=mul(M[i],ans,n,n,1);
		printf("%u
",ans.c[1][1]);
	}
	return 0;
}

p.s.

一点关于邻接矩阵的小拓展

  • 对于无权图
    (e_{i,j}=0/1)为初始的邻接矩阵
    那么(e`_{i,j}=sum_k e_{i,k}*e_{k,j})是什么意义呢
    考虑加法原理和乘法原理
    可以发现这其实就是(i)(j)长度为2的路径的条数
    以此类推可知邻接矩阵的k次方就是从一个点到一个点长度为k的路径的条数(甚至路径可以不是简单路径)

  • 对于有权图
    邻接矩阵(e_{i,j})表示直接连接i->j边的长度(若没有设为正无穷)
    我们将原来矩阵乘法中的“乘法”改为“加法”,将“加法”改为“取最小值”
    例如:(M_{i,j}=min_{k=1}^n(A_{i,k}+B_{k,j}))
    于是乎初始邻接矩阵的n次幂后得到的结果就是任意两点之间的最短路
    这个东西似乎和Floyd有不少联系呢

原文地址:https://www.cnblogs.com/zmyzmy/p/13778050.html