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 }

 

posted @ 2018-03-28 09:25  CHerish_OI  阅读(184)  评论(0编辑  收藏  举报