P2151 HH去散步
题目
emm写这个题的题解不是因为它有多难,只是提醒一下自己要灵活一些……
这个题一眼矩阵乘法。但是——仔细点!题干要求不能走回头路(就是一条边不能连续走两次)。你会痛苦地发现,普通的矩阵快速幂是无法随时变换邻接矩阵的,所以这个题没法做所以你只能考虑别的方式。
这时候一个小trick——边点互换!这样子是不是就不会考虑重复了?另外,由于边是无向边,直接当点的话无法确定这条边是到哪个点的,故一条边要拆成两条来看,当作有向边。剩下的就是普通的矩阵加速dp了。
转移方程:
\(dp_{i, u} = \sum dp_{i-1, v}\)
其中 \(v\) 是所有与 \(u\) 相连的边。
最后注意一点点细节就好了
Code:
#include<bits/stdc++.h>
using namespace std;
const int N = 130, mod = 45989;
inline int read(){
int x = 0; char ch = getchar();
while(ch<'0'||ch>'9'){
ch = getchar();
}
while(ch>='0'&&ch<='9'){
x = x*10+ch-48;
ch = getchar();
}
return x;
}
void add(int &a, int b){
a = (1ll*a+b)%mod;
}
int n, m, T, st, ed;
int len;
struct mat{
int f[N][N];
mat(){
memset(f, 0, sizeof(f));
}
int * operator [](int x){return f[x];}
void build(){
for(int i = 0; i<len; i++){
f[i][i] = 1;
}
}
mat operator * (mat B){
mat t, BT;
for(int i = 0; i<len; i++){
for(int j = 0; j<len;j++){
BT[i][j] = B[j][i];
}
}
for(int i = 0; i<len; i++){
for(int j = 0; j<len;j++){
for(int l = 0; l<len; l++){
add(t[i][j], (1ll*BT[j][l]*f[i][l])%mod);
}
}
}
return t;
}
void print(){
for(int i = 0; i<len; i++, puts("")){
for(int j = 0; j<len; j++){
printf("%d ", f[i][j]);
}
}
}
};
mat fpow(mat a, int b){
mat ret;
ret.build();
while(b){
if(b&1){
ret = ret*a;
}
b>>=1;
a = a*a;
}
return ret;
}
mat s, ans;
vector<int> e[55][2];//0出1入
int idx;
int main(){
n = read(), m = read(), T = read(), st = read(), ed = read();
len = m*2;
for(int i = 1; i<=m; i++){
int u = read(), v = read();
e[u][0].push_back(i-1);
e[u][1].push_back(i-1+m);
e[v][1].push_back(i-1);
e[v][0].push_back(i-1+m);
}
for(int i = 0; i<n; i++){//枚举点,当作边与边的桥梁(也就是新“边”)
for(int j = 0; j<e[i][0].size(); j++){
for(int k = 0; k<e[i][1].size(); k++){
int ru = e[i][1][k];
int chu = e[i][0][j];
if(abs(ru-chu)!=m){//注意,一条边不能通向自己的反向边。
s[ru][chu]++;
}
}
}
}
s = fpow(s, T-1);
for(int i = 0; i<e[st][0].size(); i++){
int chu = e[st][0][i];//初始化:与起点相连的所有出边为起始边,注意这时候视作你已经走出一步。
ans[chu][chu]++;
}
ans = ans*s;
int an = 0;
for(int i = 0; i<e[st][0].size(); i++){
for(int j = 0; j<e[ed][1].size(); j++){
int chu = e[st][0][i];
int ru = e[ed][1][j];
add(an, ans[chu][ru]);//将与终点相连的所有入边作为终点边,答案就是从起始边出发到终止边的所有方案。
}
}
printf("%d\n", an);
system("pause");
return 0;
}
/*
4 5 10 0 0
0 3
1 3
1 0
2 0
1 2
50
*/