bzoj 1875 [SDOI2009]HH去散步
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1875
正常套路的本质是dp[ i ]。现在有了对边的限制,就把状态改成dp[ i ][ j ]表示从 j 这条边来 i 的方案数。
空间有点多。考虑每条边不是对应n个点,只对应两个点,所以只记录dp[ j ]表示边的走过次数,把端点记在边的结构体里。
然后xnt从1开始就很好弄了。
第39和46行用邻接表而不是枚举所有边 + 定义 * 里少模一下mod 可以让3000ms+变成1800ms+。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int N=25,M=65,mod=45989; int n,m,t,st,ed,xnt=1,prn,head[N]; struct Edge{ int next,from,to; Edge(int n=0,int f=0,int t=0):next(n),from(f),to(t) {} }edge[M<<1]; struct Matrix{ int a[M<<1][M<<1]; Matrix(){memset(a,0,sizeof a);} Matrix operator*(const Matrix &b)const { Matrix c; for(int i=1;i<=xnt;i++)//xnt for(int k=1;k<=xnt;k++) for(int j=1;j<=xnt;j++) (c.a[i][j]+=a[i][k]*b.a[k][j])%=mod;////// return c; } }r,ans; void add(int x,int y) { edge[++xnt]=Edge(head[x],x,y);head[x]=xnt; edge[++xnt]=Edge(head[y],y,x);head[y]=xnt; } int main() { scanf("%d%d%d%d%d",&n,&m,&t,&st,&ed); int x,y; for(int i=1;i<=m;i++) scanf("%d%d",&x,&y),add(x,y); for(int i=2;i<=xnt;i++) for(int j=2;j<=xnt;j++)//j=2 if(edge[i].to==edge[j].from&&i!=(j^1))//则j能从i转移来 r.a[i][j]=1;//第j列:在求状态的第j个;第i行:加上状态的第i列 for(int i=head[st];i;i=edge[i].next)ans.a[1][i]=1; t--; while(t) { if(t&1)ans=ans*r; r=r*r;t>>=1; } for(int i=head[ed];i;i=edge[i].next)(prn+=ans.a[1][i^1])%=mod; printf("%d",prn); return 0; }