bzoj 1875 HH去散步

看到1e9的数据范围,不难想到是矩乘,但是按照边去设状态确实很神。
然后f[i][j]表示第j-1步走边j的方案数,我一直没看出来要减一。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>

#define md 45989
#define ll long long
#define inf (int) 1e9
#define eps 1e-8
#define N 45
#define M 150
using namespace std;
struct ju { int a[M][M],x,y;} a,ans;
struct yts{ int x,t,ne;} e[M];
int num=1;
int v[N];
void put(int x,int y)
{
num++; e[num].x=x; e[num].t=y;
e[num].ne=v[x]; v[x]=num;
}
ju operator * (ju a,ju b)
{
ju ans;
memset(ans.a,0,sizeof(ans.a));
for (int i=1;i<=a.x;i++)
for (int j=1;j<=b.y;j++)
for (int k=1;k<=a.y;k++)
ans.a[i][j]=(ans.a[i][j]+a.a[i][k]*b.a[k][j])%md;
ans.x=a.x; ans.y=a.y;
return ans;
}
void outit(ju a)
{
for (int i=1;i<=num;i++)
{
for (int j=1;j<=num;j++)
printf("%d ",a.a[i][j]);
printf("\n");
}
printf("\n");
}
int main()
{
int n,m,K,A,B;
scanf("%d%d%d%d%d",&n,&m,&K,&A,&B); K--;
for (int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
put(x,y); put(y,x);
}
for (int i=2;i<=num;i++)
{
int x=e[i].t;
for (int j=v[x];j;j=e[j].ne)
{
if (j!=(i^1)) a.a[i][j]=1;
}
}
a.x=a.y=num; //outit(a);
for (int i=1;i<=num;i++) ans.a[i][i]=1; ans.x=ans.y=num;
while (K)
{
if (K&1) ans=ans*a;
K>>=1; a=a*a;
}
//outit(ans);
for (int i=2;i<=num;i++)
if (e[i].x==A) a.a[1][i]=1; else a.a[1][i]=0;
//outit(a);
a.x=1; a.y=num;
a=a*ans;
//outit(a);
ll sum=0;
for (int i=2;i<=num;i++)
if (e[i].t==B) (sum+=a.a[1][i])%=md;
printf("%d\n",sum);
return 0;
}


你可能感兴趣的:(bzoj 1875 HH去散步)