矩乘与路径

来源:互联网 发布:java 获取运行时路径 编辑:程序博客网 时间:2024/04/28 21:48

题目

vijos-1603

思路

求矩阵的 m 次方,答案就是 mat[s][f]

证明

为什么这个做法是对的?

初始矩阵可以把 f[u][v] 想成 从点 u 到点 v 经过 1 条边的路径总数
我们根据矩阵相乘的代码来看,

for(int i=1;i<=n;++i)    for(int j=1;j<=n;++j)        for(int k=1;k<=n;++k)            c[i][j]+=a[i][k]*b[k][j];

假设 a[i][j] 为第 x 个矩阵,即保存的是从点 i 到点 j 经过 x 条边的路径方案数;b[i][j] 为第 y 个矩阵。
想象 k 为中转点,从 i 到 k 有 a[i][k] 种方案,从 k 到 j 有 b[i][k] 种方案, 那么从 i 到 j 就有 a[i][k]*b[k][j] 种方案咯。再考虑 a[i][k] 为 x 条边的情况,b[k][j] 为走 y 条边的情况,那 c[i][j] 就是 x+y 条边,这就很好理解了,从 i 处到 k 处经过了 x 边,从 k 处到 j 处经过了 y 条边,那从 i 处到 j 处就是 x+y 条边。
这就跟 floyd 的思想差不多了。

代码

#include<cstdio>#include<cstring>using namespace std;struct Mat{    int a[100][100];}unit,mat;int p,n;Mat cheng(Mat a,Mat b){    Mat c;    memset(c.a,0,sizeof c.a);    for(int i=1;i<=n;++i)    {        for(int j=1;j<=n;++j)        {            for(int k=1;k<=n;++k)            {                c.a[i][j]+=a.a[i][k]*b.a[k][j];                if(c.a[i][j]>=p) c.a[i][j]%=p;            }        }    }    return c;}Mat fast_pow(Mat A,int p){    Mat ans=unit;    for(;p;p>>=1,A=cheng(A,A))    {        if(p&1)            ans=cheng(ans,A);    }    return ans;}int main(){    scanf("%d",&n);    for(int i=1;i<=n;++i)    {        for(int j=1;j<=n;++j)        {            scanf("%d",&mat.a[i][j]);        }    }    int m,s,f;    scanf("%d%d%d%d",&m,&s,&f,&p);    for(int i=1;i<=n;++i) unit.a[i][i]=1;    mat=fast_pow(mat,m);    printf("%d\n",mat.a[s][f]);    return 0;}

题目

bzoj-1875

思路

“不会立刻沿着刚刚走来的路走回”说明不能在一条边上停留,显然要把边转化成点才能处理。
这样就变成了上面那道题目没有自环的情况。

所以从另一个角度解释这个问题,把每一条边分解成两个有方向的边,再加上化边为点的思想,就成了:
“一共有 2*m 个有向点,且构成同一条边的两点不能互相到达,移动必须沿着当前所在点指向的方向进行,问从点 a 移动 p-1 次到达点 b 有多少种不同方案?”

Q:
为什么必须要把每一条边分解成两个有方向的点?

A:
设有 a-b-c 三个节点,它们之间可以互相到达,如果一条边只变成一个点:则 u 为边 a-b,v 为边 b-c,虽然避免了自环 u->u(a->b->a)的情况,但 u->v->u 是合法的,也就是 a->b->c->b->a,但 b-c 这条边已经被重复走了,这就产生了矛盾。

从根本上讲,一条边变成一个点本身就是错误的,因为无向边有两个方向,即可以 a->b 也可以 b->a,只设一个点,那么这个点就包含了这两种情况,也就是只要踩到这个点上,就直接把这条边来回走了一遍,这本身就跟题目“不能来回走”是矛盾的。

所以我们首先就要把无向边的两个方向分开,a->b 是一个点,b->a 是一个点。这两个点不能互相到达。

代码

#include<cstdio>#include<cstring>using namespace std;struct Mat{    int a[200][200];}mat,unit; struct Edge{    int to,next;}e[200];int tot,head[100],n;void Add(int from,int to){    e[++tot].next=head[from];    e[tot].to = to;    head[from] = tot;}void bfs(){    for(int i=1;i<=tot;++i)    {        int now=e[i].to;        for(int j=head[now];j;j=e[j].next)        {            mat.a[i][j]=1;        }        if(i&1) mat.a[i][i+1]=0;        else mat.a[i][i-1]=0;    }}Mat cheng(Mat a,Mat b){    Mat c;    memset(c.a,0,sizeof c.a);    for(int i=1;i<=tot;++i)        for(int j=1;j<=tot;++j)            for(int k=1;k<=tot;++k)            {                c.a[i][j]+=a.a[i][k]*b.a[k][j];                if(c.a[i][j]>=45989) c.a[i][j]%=45989;            }    return c;}Mat fast_pow(Mat a,int p){    Mat ans=unit;    for(;p;p>>=1,a=cheng(a,a))    {        if(p&1)            ans=cheng(ans,a);    }    return ans;}int main(){    int t,a,b,m;    scanf("%d%d%d%d%d",&n,&m,&t,&a,&b);    for(int i=1;i<=m;++i)    {        int x,y;        scanf("%d%d",&x,&y);        Add(x+1,y+1);        Add(y+1,x+1);    }    bfs();    for(int i=1;i<=tot;++i) unit.a[i][i]=1;    mat=fast_pow(mat,t-1);    int ans=0;    for(int i=head[a+1];i;i=e[i].next)    {        for(int j=head[b+1];j;j=e[j].next)        {            if(j&1) ans+=mat.a[i][j+1];            else ans+=mat.a[i][j-1];            if(ans>=45989) ans%=45989;    //不要忘记 %        }    }    printf("%d\n",ans);    return 0;}
原创粉丝点击