先说一句,矩阵真是太强了,啥玩意都能干。这个题是真的牛逼,做完我仿佛都变成了一个矩阵。
题意简述
给定一个图, n < = 10 n<=10 n<=10,用邻接矩阵给出,每条边的权值是 0 , 9 0,9 0,9之间的整数( 1 , 9 1,9 1,9表示边权, 0 0 0表示不连通)。请你求出从 1 1 1到 n n n走边权和为 t t t的路径数。
思路
拆点。每个点能联通的只有 9 9 9种边权,所以拆成 9 9 9个点,拆成的点之间大的往小的连边,建出来一个$ 9n\times 9n 的 邻 接 矩 阵 。 把 这 个 矩 阵 求 快 速 幂 得 到 的邻接矩阵。 把这个矩阵求快速幂得到 的邻接矩阵。把这个矩阵求快速幂得到t 次 方 , 位 于 次方,位于 次方,位于(1,n)$位置的就是答案了。
具体的思维过程
- 如果我们的图边权是 0 , 1 0,1 0,1,那么该如何做呢?
- 如何把我们的图转化成1. 中那个图呢?
step.1 只有0,1的问题
设矩阵 f i f_i fi中的 ( x , y ) (x,y) (x,y)位置表示:从 x x x到 y y y边权和为 i i i的情况数。那么 f 1 f_1 f1应该就等于初始给定的邻接矩阵。
转移方程:枚举中转点 m m m, f n ( x , y ) f_n(x,y) fn(x,y)= f n − 1 ( x , m ) × f 1 ( m , y ) f_{n-1}(x,m)\times f_1(m,y) fn−1(x,m)×f1(m,y)
然后我们发现,这 t m tm tm不就是 f n = f n − 1 × f 1 f_n=f_{n-1}\times f_1 fn=fn−1×f1么(矩阵乘法)。如果还有一点数学基础(或者你没有,但是你对前缀和很熟悉),你会发现,这个式子很容易推出通项 f n = ( f 1 ) n f_n={(f_1)}^n fn=(f1)n
所以,在只有 0 , 1 0,1 0,1的图中,我们只要把邻接矩阵看成一个数学上的矩阵,拿矩阵快速幂求一下 t t t次方即珂。然后位于 ( 1 , n ) (1,n) (1,n)位置的数就是从 1 1 1到 n n n边权和为 t t t的方案数了。
step.2 转化
我们发现边权只有
10
10
10种,其中联通的还只有
9
9
9种,所以我们把每个点拆成
9
9
9个点。设
p
(
i
,
v
)
p(i,v)
p(i,v)表示点
i
i
i拆出来的第
v
v
v个点。为了节省空间,我们令
v
v
v是
0
0
0到
8
8
8中的整数(虽然理论上它应该是
1
1
1到
9
9
9中的整数)。(看到
p
(
i
,
v
)
p(i,v)
p(i,v)珂别想歪了,我还少一个
x
x
x和一个
i
i
i)。
然后我们还要转换边权。对于同一个 i i i,我们令 p ( i , v ) p(i,v) p(i,v)到 p ( i , v − 1 ) p(i,v-1) p(i,v−1)之间连一条权为 1 1 1的边。对于 u , v u,v u,v之间一条边权为 w w w的边,那就只要从 p ( u , 0 ) p(u,0) p(u,0)往 p ( v , w − 1 ) p(v,w-1) p(v,w−1)之间连一条权为 1 1 1的边即珂。我们发现,经过我们刚刚建的那些辅助边, p ( u , 0 ) p(u,0) p(u,0)到 p ( v , 0 ) p(v,0) p(v,0)之间的间接距离就是 w w w,而且我们现在只用了权为 0 0 0和 1 1 1的边。(虽然我说的只有权为 1 1 1的边,但是我没有加上去的边权就是 0 0 0)。
然后现在我们的图由于拆点,就变成了只有 0 0 0和 1 1 1边权的边了。套用刚刚的方法即珂。
具体实现的注意事项
- 别忘了膜 2009 2009 2009
- 拆点的转化公式: p ( i , v ) = v ∗ n + i p(i,v)=v*n+i p(i,v)=v∗n+i,所以空间复杂度 O ( 81 n ) O(81n) O(81n)
- 开 l o n g l o n g longlong longlong
代码:
#include<bits/stdc++.h>
using namespace std;
namespace Flandre_Scarlet
{
#define int long long
#define mod 2009
#define F(i,l,r) for(int i=l;i<=r;++i)
#define D(i,r,l) for(int i=r;i>=l;--i)
#define Fs(i,l,r,c) for(int i=l;i<=r;c)
#define Ds(i,r,l,c) for(int i=r;i>=l;c)
#define Tra(i,u) for(int i=G.Start(u),__v=G.To(i);~i;i=G.Next(i),__v=G.To(i))
#define MEM(x,a) memset(x,a,sizeof(x))
#define FK(x) MEM(x,0)
class Matrix//square matrix
{
#define N 112//changeable
private:
int a[N][N];
public:
//variable list
int n;//size
//initialization
Matrix()
{
memset(a,0,sizeof(a));
n=0;
}
Matrix(int _n)
{
memset(a,0,sizeof(a));
n=_n;
}
Matrix(int _n,int _x)
{_x%=mod;
n=_n;
for(int i=0;i<N;++i)
{
for(int j=0;j<N;++j)
{
a[i][j]=_x;
}
}
}
//get value
int* operator[](int i)
{
return *(a+i);
}
//set value
void Set(int x)
{x%=mod;
for(int i=0;i<N;++i)
{
for(int j=0;j<N;++j)
{
a[i][j]=x;
}
}
}
void Identity()
{
memset(a,0,sizeof(a));
for(int i=0;i<N;++i)
{
a[i][i]=1;
}
}
#undef N //112
};
Matrix operator*(Matrix x,Matrix y)
{
Matrix ans(x.n,0);
int n=ans.n;
for(int i=1;i<=n;++i)
{
for(int j=1;j<=n;++j)
{
for(int k=1;k<=n;++k)
{
ans[i][j]+=x[i][k]*y[k][j];
ans[i][j]%=mod;
}
}
}
return ans;
}
Matrix operator^(Matrix x,int p)
{
Matrix ans(x.n,1);
ans.Identity();
while(p)
{
if (p&1) ans=ans*x;
x=x*x,p>>=1;
}
return ans;
}
int n,t,a[112][112];
void Input()
{
scanf("%lld%lld",&n,&t);
F(i,1,n) F(j,1,n)
{
scanf("%1lld",&a[i][j]);
}
}
Matrix f(91,0);
int pos(int i,int v)
{
return v*n+i;
}
void Soviet()
{
F(i,1,n)
{
F(j,1,8) f[pos(i,j)][pos(i,j-1)]=1;
F(j,1,n)
{
int x=a[i][j];
f[i][pos(j,x-1)]=1;
}
}
f=f^t;
printf("%lld\n",f[1][n]);
}
void IsMyWife()
{
Input();
Soviet();
}
#undef int //long long
}
int main()
{
Flandre_Scarlet::IsMyWife();
getchar();getchar();
return 0;
}

本文介绍了一种使用矩阵快速幂求解特定图论问题的方法。通过将原图拆点并转换边权,构建新的邻接矩阵,利用矩阵快速幂求解从起点到终点边权和为特定值的路径数量。这种方法适用于边权为0至9的图,通过拆点和转换,将问题转化为只包含0和1边权的图,便于应用矩阵快速幂。
442

被折叠的 条评论
为什么被折叠?



