• [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有不少联系呢

  • 相关阅读:
    TongJI Online Judge预赛(3): Game
    堆栈小应用:配对
    在.net中使用Udp协议创建简单的聊天程序
    TongJI Online Judge预赛(2): LOVE LETTER
    全排列问题之递归求解
    如何打造RSS阅读器
    Html 常用标志总结
    实现页面的分帧显示
    每天OnlineJudge之 “数素数”
    文本编辑器中,如何设计 撤销/重复栈
  • 原文地址:https://www.cnblogs.com/zmyzmy/p/13778050.html
Copyright © 2020-2023  润新知