【题意】
你要做的是对于每一个起始点
s
以及每一个总时长
t(1<=t<=Q
且为整数
)
求出落叶在起始点为
s
且经过
t
单位时间后的位置也是
s
的方案数。
两个方案不同,当且仅当两个方案中存在至少一个时刻落叶所经过的边不是图G中的同一条边。
为了便于检验,只需要输出所有情况(即所有不同的起始点和总时长,一共n*Q种情况)的方案数对给定正整数P取模后的异或和即可——也就是说,假设所有情况的方案数分别是ans1、ans2、ans3……你要输出的就是(ans1 mod P) xor (ans2 mod P) xor (ans3 mod P)……
【题解】实际上是矩阵乘法:设邻接矩阵为G,我们要知道的其实就是G^1~Q这Q个矩阵的一条对角线。
预处理出G^1~根号Q和G^根号Q到Q ,那么对于任意的 G^i,都可以变成2个已有矩阵的乘积,而我们只需要其一条对角线上共n个元素,所以这一步是O(n^2)的。
【代码】
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;
const int maxn=106;
const ll inf=(ll)1<<61;
int n,t,mod,f[maxn][maxn][maxn];
int ans=0,g[maxn][maxn][maxn],h[maxn][maxn];
inline int get(){
char c;while(!isdigit(c=getchar()));
int v=c-48;while(isdigit(c=getchar()))v=v*10+c-48;
return v;
}
inline void cheng(int(*c)[maxn],int(*a)[maxn],int(*b)[maxn]){//矩阵乘法
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j){
ll tmp=0;
for(int k=1;k<=n;++k){
tmp+=(ll)a[i][k]*b[k][j];
if(tmp>=inf)tmp%=mod;
}
tmp%=mod;
c[i][j]=tmp;
}
}
inline void cal(int(*a)[maxn],int(*b)[maxn]){//两个矩阵合并来的路径长为t的矩阵
for(int i=1;i<=n;++i){
ll tmp=0;
for(int j=1;j<=n;++j){
tmp+=(ll)a[i][j]*b[j][i];
if(tmp>=inf)tmp%=mod;
}
tmp%=mod;
ans^=tmp;
}
}
int main(){
n=get();t=get();mod=get();
for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)h[i][j]=get();
int sqr=(int)sqrt(t)+1;
for(int i=1;i<=n;++i)f[0][i][i]=g[0][i][i]=1;
for(int i=1;i<sqr;++i)cheng(f[i],f[i-1],h);
cheng(g[1],f[sqr-1],h);
for(int i=2;i<=sqr;++i)cheng(g[i],g[i-1],g[1]);
for(int i=1;i<=t;++i){//共t个
cal(g[i/sqr],f[i%sqr]);//两个待合并的矩阵,自己推一下
}
printf("%lld\n",ans);
return 0;
}