题解 P5107 【能量采集】

P5107 能量采集

传送门

暴 力 草 标 算

当然也不是最基础的暴力,用矩阵快速幂倍增一下再卡卡常就轻松过去了。

前置知识

先看数据范围,$1\leqslant n\leqslant 50$,显然是让 $n^3$ 算法过的, $n^3$ 想到什么?矩阵乘法!

想到矩阵乘法就好做了,如果你不知道矩阵乘法是什么那你看为啥要看这道题,出门左转模板区矩阵快速幂矩阵加速,这里简单列出式子,不再讲解:

对于 $p\times q$ 的矩阵 $A$ 与 $q\times r$ 的矩阵 $B$,它们的乘积 $C=A\times B$ 计算方式为:

$$C_{i,j}=\sum_{k=1}^{q}A_{i,k}\times B_{k,j}$$

时间复杂度为 $O(pqr)$。


现在你了解了矩阵乘法与矩阵快速幂,那么对于这道题,我们可以使一个 $n\times1$ 的矩阵 $res$ 为初始矩阵,其中 $res_{i,1}$ 代表第 $i$ 个点最开始的能量。

对于边,我们创造一个 $n\times n$ 的转换矩阵 $base$ 为图的邻接矩阵,其中,$res_{i,1}\times base_{i,j}$ 为 $i$ 号点会流给 $j$ 号点的能量。对于边$(u,v)$,我们使 $base'_{v,u}$ 加一,最后$$base_{i,j}=\frac{base'_{i,j}}{\sum_{k=1}^nbase'_{k,j}}$$

求一个逆元即可。


对于每次询问直接上矩阵快速幂吗?时间复杂度为 $O(qn^3log_2t)≈1.8\times10^{11}$ 原地升天

那怎么办?

令$$P_i=base^{2^i}={P_{i-1}}^2$$ 每次询问的答案矩阵 $ans$ 初始值设为 $res$ ,对于每个 $i\in[0,31]$ 且 $t$ 在二进制下第 $i$ 位有为 $1$,$ans\gets P_i\times ans$ .

最终答案为$\bigoplus_{i=1}^{n}ans_{i,1}$。

时间复杂度为$O(n^3log_2t+qn^2log_2t)≈3.7\times10^9$,卡卡常就可以过了,用__int128把 $\sum_{k=1}^{q}A_{i,k}\times B_{k,j}$ 存起来最后取模可以大大减少取模次数,可以卡进 $3s$。

正解大概是转到 $k$ 进制倍增,但是我太蒟蒻了不会,可以看别的奆佬的题解。


$Code$:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
namespace IO{//IO优化
    int len=0;
    char ibuf[(1<<20)+1],*iS,*iT,out[(1<<25)+1];
    #define gh() (iS==iT?iT=(iS=ibuf)+fread(ibuf,1,(1<<20)+1,stdin),(iS==iT?EOF:*iS++):*iS++)
    #define reg register
    inline int read(){
        reg char ch=gh();
        reg int x=0;
        while(ch<'0'||ch>'9') ch=gh();
        while(ch>='0'&&ch<='9') x=x*10+(ch^48),ch=gh();
        return x;
    }
    inline void write(int x){
        if(x>9)write(x/10);
        out[len++]=x%10+48;
    }
}using namespace IO;
const int mod=998244353;
inline void add(int &a,int b){//优化加法
    a+=b;
    if(a>=mod) a-=mod;
}
struct matrix{//矩阵
    int n,m,a[55][55];
    int* operator[](const int &x){
        return a[x];
    }
    matrix(){
        memset(a,0,sizeof(a));
    }
    friend matrix operator*(const matrix &a,const matrix &b){//矩阵乘法
        matrix c;
        c.n=a.n;
        c.m=b.m;
        for(int i=1;i<=a.n;i++){
            for(int j=1;j<=b.m;j++){
                __int128 ret=0;
                for(int k=1;k<=a.m;k++){
                    ret+=1ll*a.a[i][k]*b.a[k][j];
                }
                c[i][j]=ret%mod;
            }
        }
        return c;
    }
    inline void init(int qwq){//大小为qwq*qwq的单位矩阵
        n=m=qwq;
        for(int i=1;i<=n;i++){
            a[i][i]=1;
        }
    }
}res,p[32];
inline int qpow(int x,int y){//快速幂
    int ret=1;
    while(y){
        if(y&1) ret=1ll*ret*x%mod;
        x=1ll*x*x%mod;
        y>>=1;
    }
    return ret;
}
int n,m,q;
inline void init(){
    n=read(),m=read(),q=read();
    for(int i=1;i<=n;i++){//原矩阵
        res[i][1]=read();
    }
    res.n=n;
    res.m=1;
    p[0].init(n);
    for(int i=1;i<=m;i++){
        int u=read(),v=read();
        p[0][v][u]++;//邻接矩阵
    }
    for(int i=1;i<=n;i++){
        int ret=0;
        for(int j=1;j<=n;j++){
            add(ret,p[0][j][i]);
        }
        ret=qpow(ret,mod-2);//逆元
        for(int j=1;j<=n;j++){
            p[0][j][i]=1ll*p[0][j][i]*ret%mod;
        }
    }
    for(int i=1;i<=31;i++){//预处理P
        p[i]=p[i-1]*p[i-1];
    }
}
inline void solve(){
    for(int i=1;i<=q;i++){
        int t=read();
        matrix now=res;
        for(int j=31;~j;j--){//倍增
            if(t&(1<<j)){
                now=p[j]*now;
            }
        }
        ll ans=0;
        for(int j=1;j<=n;j++){
            ans^=now[j][1];
        }
        write(ans%mod);
        out[len++]='\n';
    }
}
int main(){
    init();
    solve();
    fwrite(out,1,len,stdout);
    return 0;
}
/*
Memoty:5.49MB
Time:5.17s
*/

那么蒟蒻的这篇题解就结束了,再见$qwq$~

posted @ 2020-12-31 23:30  ffffyc  阅读(21)  评论(0)    收藏  举报  来源