Luogu P3758 可乐
本题是一个经典的矩阵快速幂好题。
但与其他题不一样的是,这个题的数据范围给的提示不是特别明显。其他题,如 POJ3735 中,(mleq 10^{10}),就是一个鲜明的提示。而此题中,(tleq 10^6),不算典型举证快速幂的数据范围,但也卡住了普通矩阵连乘的时间复杂度。
从题面中他可以留在原地,或随机前往一个相邻的点,这个条件其实可以看出用的是矩阵快速幂(有点像 P6569 [NOI Online #3 提高组] 魔法值),问题就是如何处理自爆。
我们从问题的本质出发来思考这个问题。
自爆,其实就是打断了他的双腿,不能再走动了。对啊!我们可以设置一个点 (n+1),命名为自爆点,可乐到了这里就要再也出不去了,也就是说,每个点都可以通向 (n+1),但是到了 (n+1) 这个点,就再也不能往外走了,即在 (n+1) 这个点形成了自环。
如果上述描述有点抽象,我们那样例赖建一个矩阵,如下:
[egin{bmatrix}1&1&0&1\1&1&1&1\0&1&1&1\0&0&0&1end{bmatrix}
]
快速幂计算,最后将第一行的答案累加起来即可。记得取模
//Don't act like a loser.
//You can only use the code for studying or finding mistakes
//Or,you'll be punished by Sakyamuni!!!
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const int MAXN=40,mod=2017;
int n,m,t;
struct matrix {
long long data[MAXN][MAXN];
int col,row;
void clear() {
col=row=0;
for(int i=0;i<MAXN;i++) {
for(int j=0;j<MAXN;j++) {
data[i][j]=0;
}
}
}
void initialize() {
clear();
col=row=n+1;
for(int i=1;i<=n+1;i++) {
data[i][i]=1;
}
}
inline matrix operator *(matrix& b)const {
matrix ret;
ret.clear();
for(int i=1;i<=col;i++) {
for(int k=1;k<=row;k++) {
if(data[i][k]==0) continue;
for(int j=1;j<=b.row;j++) {
ret.data[i][j]+=data[i][k]*b.data[k][j]%mod;
ret.data[i][j]%=mod;
}
}
}
ret.col=col;
ret.row=b.row;
return ret;
}
};
matrix ans;
matrix opt;
inline void qpow(int y) {
while(y) {
if(y&1) {
ans=ans*opt;
}
opt=opt*opt;
y>>=1;
}
}
signed main() {
cin>>n>>m;
opt.initialize();
for(int i=1;i<=n;i++) {
opt.data[i][n+1]=1;
}
for(int i=1;i<=m;i++) {
int u,v;
cin>>u>>v;
opt.data[u][v]=opt.data[v][u]=1;
}
ans.initialize();
opt.col=opt.row=n+1;
cin>>t;
qpow(t);
int tot=0;
for(int i=1;i<=n+1;i++) {
tot=(tot+ans.data[1][i])%mod;
}
cout<<tot<<endl;
return 0;
}