bzoj1297: [SCOI2009]迷路(矩阵乘法+拆点)
1297: [SCOI2009]迷路
题目:传送门
题解:
又是一次涨姿势
看完题目之后觉得很像DP,然后再看见T辣么大,第一个念头就是矩乘。。。
然后就不会做了....膜一发肉大佬啊
肥肠神奇,对于一个边权全都为1的邻接矩阵,直接做T次矩乘就是答案,因为1也可以当做是到该点的路径数嘛
那么这题边权并不唯一,所以要拆点,而拆点的方式和之前的一道网路流的题目很像:
因为边权是1~9的,我们直接就拆成十个点,然后拆的第i个点向第i-1个点连边(倒过来),流量就为1
对于原本两个相连的点x、y,那么就将x的第1个点和y的第i个点连,i为原本的边权
这样纸边权又全部为1啦,然后很开心的去矩乘啦啦啦~~
代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<cmath> 5 #include<algorithm> 6 #define mod 2009 7 using namespace std; 8 struct node 9 { 10 int m[150][150]; 11 node(){memset(m,0,sizeof(m));} 12 }A,B,C,pre; 13 int n,T;char st[20]; 14 node multi(node a,node b) 15 { 16 node c; 17 for(int i=1;i<=n*10;i++) 18 for(int j=1;j<=n*10;j++) 19 for(int k=1;k<=n*10;k++) 20 c.m[i][j]=(c.m[i][j]+a.m[i][k]*b.m[k][j]%mod)%mod; 21 return c; 22 } 23 node p_m(node a,int b) 24 { 25 node ans; 26 for(int i=1;i<=10;i++)ans.m[i][i]=1; 27 while(b) 28 { 29 if(b%2==1)ans=multi(ans,a); 30 a=multi(a,a);b/=2; 31 } 32 return ans; 33 } 34 int main() 35 { 36 scanf("%d%d",&n,&T); 37 for(int i=1;i<=n;i++)for(int j=2;j<=10;j++)pre.m[(i-1)*10+j][(i-1)*10+j-1]=1; 38 for(int i=1;i<=n;i++) 39 { 40 scanf("%s",st+1); 41 for(int j=1;j<=n;j++)if(st[j]!='0')pre.m[(i-1)*10+1][(j-1)*10+st[j]-'0']=1; 42 } 43 node ans=p_m(pre,T); 44 printf("%d\n",ans.m[1][(n-1)*10+1]); 45 return 0; 46 }