P2151 [SDOI2009]HH去散步
阿新 • • 發佈:2018-11-17
題目大意:問從起點走到終點,不能立刻走上一條走過的邊,長度為\(t\)的方案數
按點考慮會很麻煩,我們考慮按邊來考慮。先把無向邊給拆成兩條有向邊,記\(dp[i][j]\)表示在\(i\)時刻走過第\(j\)條邊到了\(j\)邊的終點的方案數。那麼它可以從\(j\)邊的終點繼續走,只要走的下一條邊不是\(j\)的反向邊就行了。很容易寫出dp方程
然而發現dp陣列的第一維太大了。我們考慮一下,對於每一個第二維,它能轉移到的狀態是確定的,也就是說轉移之間的係數矩陣是可以確定的,那麼我們就可以用矩陣優化了。
還有要注意,對於起點的時候是沒有不能走哪條邊的限制的,所以我們可以先列舉\(1\)
//minamoto #include<bits/stdc++.h> #define op(i) ((i&1)?(i+1):(i-1)) using namespace std; #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) char buf[1<<21],*p1=buf,*p2=buf; int read(){ int res,f=1;char ch; while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1); for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0'); return res*f; } const int N=205,mod=45989; inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;} inline int mul(int x,int y){return x*y%mod;} int head[N],Next[N],ver[N],tot; inline void add_edge(int u,int v){ver[++tot]=v,Next[tot]=head[u],head[u]=tot;} struct Matrix{ int a[N][N],n,m; inline void clr(){n=m=0;memset(a,0,sizeof(a));} Matrix(){clr();} int* operator [](const int x){return a[x];} Matrix operator *(Matrix b){ Matrix res;res.clr(),res.n=n,res.m=b.m; for(int i=1;i<=n;++i)for(int j=1;j<=b.m;++j)for(int k=1;k<=m;++k) res[i][j]=add(res[i][j],mul(a[i][k],b[k][j])); return res; } inline void operator *=(Matrix b){*this=*this*b;} }A,B; int main(){ // freopen("testdata.in","r",stdin); int n=read(),m=read(),tt=read(),op=read()+1,ed=read()+1; for(int i=1,u,v;i<=m;++i)u=read()+1,v=read()+1,add_edge(u,v),add_edge(v,u); A.n=1,A.m=B.m=B.n=tot; for(int j=1;j<=tot;++j){ int u=ver[j]; for(int i=head[u];i;i=Next[i]) if(i!=op(j))B[j][i]+=1; } for(int i=head[op];i;i=Next[i])A[1][i]+=1; for(int i=tt-1;i;i>>=1,B*=B)if(i&1)A*=B; int ans=0; for(int i=head[ed];i;i=Next[i])ans=add(ans,A[1][op(i)]); printf("%d\n",ans);return 0; }