题解 P5107 【能量采集】
P5107 能量采集
传送门
暴 力 草 标 算
当然也不是最基础的暴力,用矩阵快速幂倍增一下再卡卡常就轻松过去了。
前置知识
先看数据范围,,显然是让 算法过的, 想到什么?矩阵乘法!
想到矩阵乘法就好做了,如果你不知道矩阵乘法是什么那你看为啥要看这道题,出门左转模板区矩阵快速幂和矩阵加速,这里简单列出式子,不再讲解:
对于 的矩阵 与 的矩阵 ,它们的乘积 计算方式为:
时间复杂度为 。
现在你了解了矩阵乘法与矩阵快速幂,那么对于这道题,我们可以使一个 的矩阵 为初始矩阵,其中 代表第 个点最开始的能量。
对于边,我们创造一个 的转换矩阵 为图的邻接矩阵,其中, 为 号点会流给 号点的能量。对于边,我们使 加一,最后
求一个逆元即可。
对于每次询问直接上矩阵快速幂吗?时间复杂度为 原地升天。
那怎么办?
令 每次询问的答案矩阵 初始值设为 ,对于每个 且 在二进制下第 位有为 , .
最终答案为。
时间复杂度为,卡卡常就可以过了,用__int128
把 存起来最后取模可以大大减少取模次数,可以卡进 。
正解大概是转到 进制倍增,但是我太蒟蒻了不会,可以看别的奆佬的题解。
:
#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
*/
那么蒟蒻的这篇题解就结束了,再见~
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)