洛谷 P2151 [SDOI2009]HH去散步

题目链接

思路

如果没有不能走上一条边的限制,很显然就是dp。

设f[i][j]表示到达i点走了j步的方案数,移到k点可以表示为f[k][j+1]+=f[i][j]。

如果有限制的话,可以考虑用边表示将之前思路中的i变为边的终点,只要不走同一条边,转移还是相同的。

但是t ≤ 2^30,显然直接dp是不可行的,这是机智的题解就想到了矩阵优化。

由于递推关系是线性的,可以搞一个行矩阵表示对于当前移动步数各个边的方案数。

转移可以考虑构造另一个矩阵,所有可以更新的边之间都变为1,其他是0,对于每一次转移都是一样的,所以只要将矩阵跑t次就可以了。

对于初始矩阵,可以加一条边设一个不存在的点,将它与起点相连,也方便之后的领接表。

此题疯狂卡时,辣鸡出题人。(据说多交几次就可以过

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 using namespace std;
 5 const int mod=45989;
 6 int n,m,t,e,s,cnt,hd[125],sum;
 7 struct edge
 8 {
 9     int to,from,nxt;
10 }v[125];
11 struct mat
12 {
13     int a[125][125];
14     mat()
15     {
16         memset(a,0,sizeof(a));
17     }
18     mat operator * (mat x)
19     {
20         mat ans;
21         for(int i=1;i<=cnt;i++)
22             for(int j=1;j<=cnt;j++)
23                 for(int k=1;k<=cnt;k++)
24                     ans.a[i][j]=(ans.a[i][j]+a[i][k]*x.a[k][j])%mod;
25         return ans;
26     }
27 }c,ans;
28 void addedge(int x,int y)
29 {
30     v[++cnt].nxt=hd[x];
31     v[cnt].from=x;
32     v[cnt].to=y;
33     hd[x]=cnt;
34 }
35 mat mul(mat x,int k)
36 {
37     mat res;
38     for(int i=1;i<=cnt;i++)
39         res.a[i][i]=1;
40     for(int i=k;i;i>>=1,x=x*x)
41         if(i&1)
42             res=res*x;
43     return res;
44 }
45 int main()
46 {
47     scanf("%d%d%d%d%d",&n,&m,&t,&s,&e);
48     s++,e++;
49     addedge(0,s);
50     for(int i=1;i<=m;i++)
51     {
52         int x,y;
53         scanf("%d%d",&x,&y);
54         x++,y++;
55         addedge(x,y),addedge(y,x);
56     }
57     for(int i=1;i<=cnt;i++)
58         for(int j=1;j<=cnt;j++)
59             if((i^j)!=1&&v[i].to==v[j].from)
60                 c.a[i][j]=1;
61     ans.a[1][1]=1;
62     c=mul(c,t);
63     ans=ans*c;
64     for(int i=1;i<=cnt;i++)
65         if(v[i].to==e)
66             sum=(sum+ans.a[1][i])%mod;
67     printf("%d
",sum);
68     return 0;
69 }
View Code
原文地址:https://www.cnblogs.com/fantasquex/p/9377960.html