跑步(walk-矩阵加速图-点边互换)

跑步(walk)

题目背景:

小白非常喜欢跑步,所以他经常在校园内跑步(其实是想看美女~)。校园可以看成由N个地区,由M个道路连接。我们小白早上从一个地点出发,但是不知道怎么跑才好。小白有个习惯,不会沿着刚刚经过的道路再返回(比如从A到B经过C道路,下一次,不会再沿着C从B返回A)。

小白想知道从他出发的地点,经过Q条路,到达每个点的方案数。这样方便他去选择。

输入格式:

第一行3个整数N,M,S,Q。表示有N个地区,M条道路,从S出发,需要经过Q条路。

下面M行,每行两个整数表示A,B之间有条道路。

输出格式:

一共N行,每行一个整数表示从S到I的方案数(mod 45989)

Input

1020 9 10

1 5

5 10

10 4

10 2

10 7

4 3

10 9

2 8

5 6

6 1

2 10

4 7

9 10

9 6

7 3

7 3

7 2

1 8

9 7

4 5

Output

17420

41928

35701

40814

31937

22933

5754

15848

43620

10819

 

我们发现M很小

于是点代边,边带点,成功解决限制条件


#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>
#include<cmath>
#include<cctype>
using namespace std;
#define For(i,n) for(int i=1;i<=n;i++)
#define Rep(i,n) for(int i=0;i<n;i++)
#define Fork(i,k,n) for(int i=k;i<=n;i++)
#define ForD(i,n) for(int i=n;i;i--)
#define Forp(x) for(int p=pre[x];p;p=next[p])
#define RepD(i,n) for(int i=n;i>=0;i--)
#define MAXN (30+10)
#define MAXM (60+10)
#define F (45989)
int n,m,s,q;
int un(int i){if (i%2) return i+1;return i-1;}
struct M
{
	long long a[MAXM*2][MAXM*2];
	M(){For(i,120) For(j,120) a[i][j]=0;}
	long long& operator()(int i,int j){return a[i][j];	}
	friend M operator*(M a,M b)
	{
		M c;
		For(i,2*m) For(j,2*m) For(k,2*m) c(i,j)=(c(i,j)+a(i,k)*b(k,j))%F; 
		return c;
	}
	void print()
	{
		For(i,2*m)
		{
			For(j,2*m) printf("%d ",a[i][j]);
			puts("");
		}
	}	
}a0,c;
int a2[MAXN],size=0;
M pow(M a,int b)
{
	size=0;
	while (b) {a2[++size]=b%2,b/=2;}
	c=a0;
	ForD(i,size-1)
	{
		if (a2[i]==1) c=c*c*a;
		else c=c*c;
	}
	return c;
}
struct edge
{
	int x,y;
}p[MAXM*2];
int ans[MAXN]={0};
int main()
{
	freopen("walk.in","r",stdin);
	freopen("walk.out","w",stdout);
	scanf("%d%d%d%d",&n,&m,&s,&q);
	For(i,m) 
		scanf("%d%d",&p[(i<<1)-1].x,&p[(i<<1)-1].y),p[i<<1].x=p[(i<<1)-1].y,p[i<<1].y=p[(i<<1)-1].x;
	q--;
	For(i,2*m)
		For(j,2*m)
			if (un(i)^j) a0(i,j)=(int)(p[i].y==p[j].x);
	
//	a0.print();
	a0=pow(a0,q);
//	a0.print();
	For(i,2*m)
		For(j,2*m) if (p[i].x==s) ans[p[j].y]=(ans[p[j].y]+a0(i,j))%F;
	For(i,n) cout<<ans[i]<<endl;
	return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值