Luogu P2151 [SDOI2009]HH去散步 矩乘加速DP

思路:矩乘优化DP

提交:3次(用了一个奇怪的东西导致常数过大)

题解:

如果可以走完正向边后又走反向边那就显然了,但是不能走,所以我们要将正反向边分别编号,区分正反向边。

所以这道题的矩阵是以边的编号(边的邻接矩阵),而非点来DP的。

具体地,记录每个边$w_i=(u_i,v_i)$和$w_{i^1}=(v_{i^1},u_{i^1})$,注意这个有向的。

设起点为$s$,终点为$t$,我们的初始矩阵$S$是一根行向量,把所有的$w_i and u_i==s $设为$1$,表示$s$与$w_i$连通;

然后转移矩阵$a$:若$v_i==u_j and i!=j xor 1$ ,即两条边相连,但不是互为正反向边,则在边的邻接矩阵中设为$1$。

然后快速幂,并令$ans=S*a$

最后求解答案时,统计所有$ans$中$w_i and v_i==t$ 的答案,即为最终的答案。

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#define R register int
using namespace std;
//你弱,有什么资格休息
#define ull unsigned long long
#define ll long long
#define pause (for(R i=1;i<=10000000000;++i))
#define In freopen("NOIPAK++.in","r",stdin)
#define Out freopen("out.out","w",stdout)
namespace Fread {
static char B[1<<15],*S=B,*D=B;
#ifndef JACK
#define getchar() (S==D&&(D=(S=B)+fread(B,1,1<<15,stdin),S==D)?EOF:*S++)
#endif
inline int g() {
    R ret=0,fix=1; register char ch; while(!isdigit(ch=getchar())) fix=ch=='-'?-1:fix;
    if(ch==EOF) return EOF; do ret=ret*10+(ch^48); while(isdigit(ch=getchar())); return ret*fix;
} inline bool isempty(const char& ch) {return (ch<=36||ch>=127);}
inline void gs(char* s) {
    register char ch; while(isempty(ch=getchar()));
    do *s++=ch; while(!isempty(ch=getchar()));
}
} using Fread::g; using Fread::gs;

namespace Luitaryi {
const int N=130,M=45989;
int n,m,p,S,T,cnt=-1,x[N],y[N];
int a[N][N],s[2][N],ans[2][N],anss;
inline void mul(int a[][N],int b[][N]) { R tmp[N][N]; memset(tmp,0,sizeof(tmp));
    for(R i=0;i<=cnt;++i) for(R k=0;k<=cnt;++k) for(R j=0;j<=cnt;++j)    
        (tmp[i][j]+=1ll*a[i][k]*b[k][j]%M)%=M;
    memcpy(a,tmp,sizeof(tmp));
}
inline void qpow(int p) {
    R ret[N][N]; memset(ret,0,sizeof(ret));
    for(R i=0;i<=cnt;++i) ret[i][i]=1;
    for(;p;p>>=1,mul(a,a)) if(p&1) mul(ret,a);
    memcpy(a,ret,sizeof(ret));
}
inline void main() {
    n=g(),m=g(),p=g(),S=g(),T=g();
    for(R i=1;i<=m;++i) {
        R u=g(),v=g();
        x[++cnt]=u,y[cnt]=v;
        x[++cnt]=v,y[cnt]=u;
    } for(R i=0;i<=cnt;++i) for(R j=0;j<=cnt;++j) if(i!=(j^1)&&y[i]==x[j]) a[i][j]+=1;
    for(R i=0;i<=cnt;++i) if(x[i]==S) s[1][i]=1;
    qpow(p-1); for(R i=0;i<=cnt;++i) for(R j=0;j<=cnt;++j) (ans[1][j]+=1ll*s[1][i]*a[i][j]%M)%=M;
    for(R i=0;i<=cnt;++i) if(y[i]==T) (anss+=ans[1][i])%=M; printf("%d
",anss);
}
} 
signed main() {
    Luitaryi::main();
}

2019.07.20

原文地址:https://www.cnblogs.com/Jackpei/p/11217473.html