P5107 能量采集
观察数据范围发现 \(n\) 很小而 \(t\) 很大,于是我们想到矩阵快速幂。我们可以直接建 \(n \times n\) 的转移矩阵 \(A\),设 \(u\) 的出边为 \(d_u\) 条,边 \((u,v)\) 共有 \(cnt_{(u,v)}\) 条(注意重边要计算多次即 \(cnt_{(u,v)}\) 可能大于一),则 \(A_{v,u} \equiv \dfrac{cnt_{(u,v)}}{d_u} \pmod {998244353}\),再定义 \(n \times 1\) 的初始矩阵 \(B\) 其中 \(B_{i,1} \equiv a_i \pmod {998244353}\)。则询问 \(t\) 的答案即为 \(A^t \times B\)。此时时间复杂段为 \(\mathcal{O}(qn^3 \log t)\),卡卡常能过不太能过。
我们考虑进行优化。我们考虑快速幂的过程,据一个例子 \(t=13\),则我们实际计算的是 \(A^8 \times A^4 \times A^1 \times B\)。而矩阵乘法具有结合律,我们可以优化计算顺序:\(A^8 \times (A^4 \times (A^1 \times B))\),我们发现 \(A^{2^k}\) 与 \(B\) 相乘的复杂度仅为 \(\mathcal{O}(n^2)\),而由于转移矩阵是已知的,那么 \(A^{2^k}\) 是可以预处理的。于是我们成功把时间复杂度降为了 \(\mathcal{O}(n^3 \log t + qn^2 \log t)\),能过。
// 火车头
const int mod=998244353;
const int N=55;
struct Matrix {
int n,m,a[N][N];
Matrix(int l=0,int c=0) {
n=l,m=c,memset(a,0,sizeof(a));
return ;
}
inline void init(int d) {
n=m=d,memset(a,0,sizeof(a));
for(int i=1;i<=d;i++) a[i][i]=1;
return ;
}
inline Matrix operator *(const Matrix &tmp) const {
Matrix res(n,tmp.m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=1;k<=tmp.m;k++)
add(res.a[i][k],(ll)a[i][j]*tmp.a[j][k]%mod);
return res;
}
};
int n,m,q;
vector<int> edge[N];
Matrix ret[30],stat;
inline ll quickpow(ll base,ll p) {
ll tmp=1;
for(;p;p>>=1) {
if(p&1) tmp=tmp*base%mod;
base=base*base%mod;
}
return tmp;
}
inline Matrix quickpow_matrix(ll p) {
Matrix tmp=stat;
for(int i=0;i<30;i++)
if((p>>i)&1) tmp=ret[i]*tmp;
return tmp;
}
int main() {
read(n),read(m),read(q);
stat=Matrix(n,1);
for(int i=1;i<=n;i++) read(stat.a[i][1]),edge[i].pb(i);
for(int i=1,u,v;i<=m;i++) read(u),read(v),edge[u].pb(v);
ret[0]=Matrix(n,n);
for(int i=1;i<=n;i++) {
ll c=quickpow(edge[i].size(),mod-2);
for(int v:edge[i]) add(ret[0].a[v][i],c);
}
for(int i=1;i<30;i++) ret[i]=ret[i-1]*ret[i-1];
for(int i=1,t;i<=q;i++) {
read(t);
Matrix res=quickpow_matrix(t);
ll ans=0;
for(int i=1;i<=n;i++) ans^=res.a[i][1];
write(ans%mod),_E;
}
return 0;
}
点个赞再走喵 qaq

浙公网安备 33010602011771号