1. 程式人生 > >HH去散步[SDOI2009]

HH去散步[SDOI2009]

ons logs blog 完全 理論 多余 出發點 題解 temp

題目描述

HH有個一成不變的習慣,喜歡飯後百步走。所謂百步走,就是散步,就是在一定的時間 內,走過一定的距離。 但是同時HH又是個喜歡變化的人,所以他不會立刻沿著剛剛走來的路走回。 又因為HH是個喜歡變化的人,所以他每天走過的路徑都不完全一樣,他想知道他究竟有多 少種散步的方法。 現在給你學校的地圖(假設每條路的長度都是一樣的都是1),問長度為t,從給定地 點A走到給定地點B共有多少條符合條件的路徑

輸入

第一行:五個整數N,M,t,A,B。 N表示學校裏的路口的個數 M表示學校裏的 路的條數 t表示HH想要散步的距離 A表示散步的出發點 B則表示散步的終點。 接下來M行 每行一組Ai,Bi,表示從路口Ai到路口Bi有一條路。 數據保證Ai != Bi,但不保證任意兩個路口之間至多只有一條路相連接。 路口編號從0到N -1。 同一行內所有數據均由一個空格隔開,行首行尾沒有多余空格。沒有多余空行。 答案模45989。 N ≤ 20,M ≤ 60,t ≤ 2^30,0 ≤ A,B

輸出

一行,表示答案。

樣例輸入

4 5 3 0 0
0 1
0 2
0 3
2 1
3 2

樣例輸出

4

題解
    考試的時候想得比較粗略,一開始當然是暴力深搜,也知道這種方案數還取模的題不可能深搜出正解,但還是先打了一通,沒費什麽力氣就出了樣例。之後開始想正解,感覺或許應該把環收縮一下,但是環環相扣環環重疊不知道應該怎麽處理,最後還是把深搜交上去了。
    正解是矩陣優化dp(但總感覺它其實說不上是dp),比如矩陣G[i,j]的k次方中的g(a,b)可以表示從a點到b點經過k條邊的方案數,本題因為對於邊有特殊的要求(不立刻反向)所以把邊放進矩陣,G[i,j]為1表示從i到j有一條邊,這裏的邊是分方向的,雙向建邊就會有2*m條。tot矩陣(其實只有一行)表示從起點有哪些邊出來。tot乘上G[i,j]的k-1次方(註意順序,矩陣乘法不滿足交換律),把得到的一行結果矩陣中有邊到終點的位加和,得到的結果即為答案。
    剛開始覺得矩陣非常抽象,雖然自己在課下學過很多數學書上的理論知識但還是不會用,不過這道題讓我明白矩陣也是有明確含義的(加速矩陣除外),本質上還是要理解算法每一步的目的。


#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,m,t,a,b,temp;
const int md=45989;
int dis[125][125]={0},tot[2][125]={0},ans[125][125]={0};
inline int r()
{
    int jg=0,jk=0;
    jk=getchar()-0;
    if(jk>=0&&jk<=9)  jg+=jk;
    jk=getchar()-
0; while(jk>=0&&jk<=9) { jg*=10; jg+=jk; jk=getchar()-0; } return jg; } int h[25],e; struct B { int u,v,ne; }bi[125]; void add(int x,int y) { bi[e].u=x; bi[e].v=y; bi[e].ne=h[x]; h[x]=e++; } int jg[125][125]; void jc(int cs1[][125],int cs2[][125]) { memset(jg,0,sizeof(jg)); for(int i=0;i<temp;i++) for(int j=0;j<temp;j++) for(int k=0;k<temp;k++) jg[i][j]+=(cs1[i][k]*cs2[k][j])%md; for(int i=0;i<temp;i++) for(int j=0;j<temp;j++) cs1[i][j]=jg[i][j]%md; } void init() { n=r(); m=r(); t=r(); a=r(); b=r(); temp=2*m; memset(h,-1,sizeof(h)); int a1,a2; for(int i=0;i<m;i++) { a1=r(); a2=r(); add(a1,a2); add(a2,a1); } for(int i=0;i<n;i++) for(int j=h[i];j!=-1;j=bi[j].ne) for(int k=h[bi[j].v];k!=-1;k=bi[k].ne) { if(((k&1)&&k==j+1)||((j&1)&&j==k+1)) continue; dis[j][k]=1; } for(int i=h[a];i!=-1;i=bi[i].ne) tot[0][i]=1; } int main() { init(); for(int i=0;i<temp;i++) ans[i][i]=1; t--; while(t) { if(t&1) jc(ans,dis); t>>=1; jc(dis,dis); } jc(tot,ans); int res=0; for(int i=h[b];i!=-1;i=bi[i].ne) { if(i&1) res+=tot[0][i-1]; else res+=tot[0][i+1]; } printf("%d",res%md); return 0; }

 

HH去散步[SDOI2009]