题解 P5107 【能量采集】

P5107 能量采集

传送门

暴 力 草 标 算

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

前置知识

先看数据范围,1n50,显然是让 n3 算法过的, n3 想到什么?矩阵乘法!

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

对于 p×q 的矩阵 Aq×r 的矩阵 B,它们的乘积 C=A×B 计算方式为:

Ci,j=k=1qAi,k×Bk,j

时间复杂度为 O(pqr)


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

对于边,我们创造一个 n×n 的转换矩阵 base 为图的邻接矩阵,其中,resi,1×basei,ji 号点会流给 j 号点的能量。对于边(u,v),我们使 basev,u 加一,最后basei,j=basei,jk=1nbasek,j

求一个逆元即可。


对于每次询问直接上矩阵快速幂吗?时间复杂度为 O(qn3log2t)1.8×1011 原地升天

那怎么办?

Pi=base2i=Pi12 每次询问的答案矩阵 ans 初始值设为 res ,对于每个 i[0,31]t 在二进制下第 i 位有为 1ansPi×ans .

最终答案为i=1nansi,1

时间复杂度为O(n3log2t+qn2log2t)3.7×109,卡卡常就可以过了,用__int128k=1qAi,k×Bk,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 @   ffffyc  阅读(7)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示