您的位置:首页 > 其它

[BZOJ1875][SDOI2009]HH去散步(矩阵快速幂)

2016-05-23 20:49 253 查看

题目描述

传送门

题解

对于读进来的两个点x,y,x->y,y->x;

对于三个点x,y,z,如果存在边i使x->y,边j使y->z,那么在邻接矩阵G里将(i,j)赋为1.

求Gt−1即可.

解释一下,这里将一条双向边拆成两条单向边,并且只有连续的连边关系才能在邻接矩阵里标记,目的是不能走回头路.

构造好邻接矩阵之后,相当于构造出了一个新的图,这个图里的点相当于是原图里的一条边,那么在原图里恰好要经过t条边,在新的图里就要经过t个点,即t-1条边.并且这样的建图方式保证在新图里也不会走回头路,因为两条边不可能互相连边。

感觉这个思路很厉害呀。

代码

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;

const int max_n=25;
const int max_m=65;
const int max_N=max_m*2;
const int max_M=max_N*max_N;
const int max_e=max_M*2;
const int Mod=45989;

int n,m,t,st,end,x,y,final;
int tot,point[max_N],nxt[max_e],v[max_e];
struct hp{int a[max_N][max_N];}G,unit,ans;

inline void addedge(int x,int y)
{
++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
}
inline hp cheng(hp a,hp b)
{
hp ans;
memset(ans.a,0,sizeof(ans.a));
for (int i=0;i<=tot;++i)
for (int j=0;j<=tot;++j)
for (int k=0;k<=tot;++k)
ans.a[i][j]=(ans.a[i][j]+a.a[i][k]*b.a[k][j]%Mod)%Mod;
return ans;
}
inline hp matrix_fast_pow(hp a,int p)
{
hp ans=unit;
for (;p;p>>=1,a=cheng(a,a))
if (p&1)
ans=cheng(ans,a);
return ans;
}
int main()
{
scanf("%d%d%d%d%d",&n,&m,&t,&st,&end);
st++; end++;
tot=-1; memset(point,-1,sizeof(point)); memset(nxt,-1,sizeof(nxt));
for (int i=1;i<=m;++i)
{
scanf("%d%d",&x,&y); x++; y++;
addedge(x,y);
}
for (int i=0;i<=tot;++i) unit.a[i][i]=1;
for (int i=0;i<=tot;++i)
for (int j=point[v[i]];j!=-1;j=nxt[j])
if (i!=(j^1))
G.a[i][j]++;
ans=matrix_fast_pow(G,t-1);
for (int i=point[st];i!=-1;i=nxt[i])
for (int j=point[end];j!=-1;j=nxt[j])
final=(final+ans.a[i][j^1])%Mod;
printf("%d\n",final);
}


总结

首先这道题新学到的思想:化边为点,双向边拆两条单向边。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: