【洛谷P6569】魔法值

题目

题目链接:https://www.luogu.com.cn/problem/P6569
H 国的交通由 \(n\) 座城市与 \(m\) 条道路构成,城市与道路都从 \(1\) 开始编号,其中 \(1\) 号城市是 H 国的首都。H 国中一条道路将把两个不同城市直接相连,且任意两个城市间至多有一条道路。

H 国是一个信奉魔法的国家,在第 \(j\) 天,\(i\) 号城市的魔法值为 \(f_{i,j}\)。H 国的魔法师已观测到第 0 天时所有城市的魔法值 \(f_{i,0}\),且他们还发现,之后的每一天每个城市的魔法值,都将会变为所有与该城市直接相连的城市的前一天魔法值的异或值,即

\[f_{x,j}=f_{v_1,j-1}\oplus f_{v_2,j-1}\oplus \cdots\oplus f_{v_k,j-1} \]

其中 \(j\ge 1\)\(v_1,v_2,\cdots,v_k\) 是所有与 \(x\) 号城市直接相连的城市,\(\oplus\) 为异或运算。

现在 H 国的国王问了你 \(q\) 个问题,对于第 \(i\)\(1\le i\le q\))个问题你需要回答:第 \(a_i\) 天时首都的魔法值是多少。

思路

显然第 \(t\) 天城市 \(i\) 的魔法值 \(f_{i,t}\) 为所有距离 \(i\) 长度为 \(t\) 且道路有奇数条的点 \(j\) 的第 0 天权值异或和。

那么这个玩意可以用矩阵乘法求,对于每一个询问跑一次矩阵乘法,时间复杂度 \(O(qn^3 \log t)\)

但是我们发现每次初始矩阵都是一样的,只是矩阵乘法的次数不同,而且题目中只要求点 1 的魔法值,所以我们可以预处理出初始矩阵自乘 \(2^k(0\leq k\leq 31)\) 次的矩阵 \(a[k]\),然后对于每一个询问 \(t\),第 \(t\) 天对点 1 有贡献的点就是 \(t\) 二进制下为 1 的位置的矩阵相乘后,矩阵中为 1 的点集。

考虑到只要求点 1 的魔法值,所以最终每个询问的矩阵是一个 \(1\times n\) 的矩阵,此时单次矩阵乘法时间复杂度为 \(O(n^2\log t)\)

这样总时间复杂度为 \(O(n^3\log t+qn^2 \log t)\)

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;

const int N=110,LG=31;
int n,m,Q;
ll f[N];
bool flag;

struct Matrix
{
	int a[N][N];
		
	friend Matrix operator *(Matrix a,Matrix b)
	{
		Matrix c;
		memset(c.a,0,sizeof(c.a));
		for (int i=1;i<=(flag?1:n);i++)
			for (int j=1;j<=n;j++)
				for (int k=1;k<=n;k++)
					c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%2;
		return c;
	}
}a[LG+1],mat;

int main()
{
	scanf("%d%d%d",&n,&m,&Q);
	for (int i=1;i<=n;i++)
		scanf("%lld",&f[i]);
	for (int i=1,x,y;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		a[0].a[x][y]=a[0].a[y][x]=1;
	}
	for (int l=1;l<=LG;l++)
		a[l]=a[l-1]*a[l-1];
	flag=1;
	while (Q--)
	{
		ll p,ans=0;
		scanf("%lld",&p);
		memset(mat.a,0,sizeof(mat.a));
		mat.a[1][1]=1;
		for (int i=LG;i>=0;i--)
			if (p>=(1LL<<i))
			{
				mat=mat*a[i];
				p-=(1LL<<i);
			}
		for (int i=1;i<=n;i++)
			ans^=(1LL*f[i]*mat.a[1][i]);
		printf("%lld\n",ans);
	}
	return 0;
}
posted @ 2020-05-27 21:02  stoorz  阅读(...)  评论(...编辑  收藏