【bzoj1875】 SDOI2009 HH去散步 dp+矩阵乘法

SDOI的好题,今天听豪爷讲课的时候第一眼没有想出来,一般对10^9比较敏感,就能想到矩乘,但是一看2^30就开始想什么倍增了。

f[i][2][k]表示刚走完第i条边正着走还是倒着走,走过k距离的方案数。

转移是:f[i][0][k]-->f[j][0][k+1]  i.y==j.x
            -->f[j][1][k+1]  i.y==j.y
      f[i][1][k]-->f[j][0][k+1]  i.x==j.x
    -->f[j][1][k+1]  i.x==j.y

然后矩乘就好了。


#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#define mod 45989

using namespace std;

struct matrix
{
	int x,y;
	int a[121][121];
	matrix operator* (matrix b)
	{
		matrix ans;
		memset(ans.a,0,sizeof(ans.a));
		ans.x=x;ans.y=b.y;
		for (int i=1;i<=ans.x;i++)
		  for (int j=1;j<=ans.y;j++)
		    for (int k=1;k<=y;k++)
		      ans.a[i][j]=(ans.a[i][j]+a[i][k]*b.a[k][j])%mod;
		return ans;
	}
};

int x[61],y[61];
int n,m,k,s,t;

int main()
{
	scanf("%d%d%d%d%d",&n,&m,&k,&s,&t);
	for (int i=1;i<=m;i++)
	  scanf("%d%d",&x[i],&y[i]);
	matrix a,b;
	a.x=1,a.y=2*m;
	memset(a.a,0,sizeof(a.a));
	b.x=2*m;b.y=2*m;
	memset(b.a,0,sizeof(b.a));
	for (int i=1;i<=m;i++)
	{
		if (x[i]==s) a.a[1][i]=1;
		if (y[i]==s) a.a[1][i+m]=1;
	}
	for (int i=1;i<=m;i++)
	  for (int j=1;j<=m;j++)
	  {
	  	if (i==j) continue;
	  	if (y[i]==x[j]) b.a[i][j]++;
	  	if (y[i]==y[j]) b.a[i][j+m]++;
	  	if (x[i]==x[j]) b.a[i+m][j]++;
	  	if (x[i]==y[j]) b.a[i+m][j+m]++;
	  }
	matrix ans;
	memset(ans.a,0,sizeof(ans.a));
	ans.x=2*m;ans.y=2*m;
	for (int i=1;i<=ans.x;i++) ans.a[i][i]=1;
	k--;
	while (k>0)
	{
		if (k&1) ans=ans*b;
		b=b*b;
		k>>=1;
	}
	ans=a*ans;
	int sum=0;
	for (int i=1;i<=m;i++)
	{
		if (x[i]==t) sum=(sum+ans.a[1][i+m])%mod;
		if (y[i]==t) sum=(sum+ans.a[1][i])%mod;
	}
	printf("%d\n",sum);
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值