1. 程式人生 > >HDU2157(矩陣的一種 經典問題)

HDU2157(矩陣的一種 經典問題)

 給定一個有向圖,問從A點恰好走k步(允許重複經過邊)到達B點的方案數mod p的值
        把給定的圖轉為鄰接矩陣,即A(i,j)=1當且僅當存在一條邊i->j。令C=A*A,那麼C(i,j)=ΣA(i,k)*A(k,j),實際上就等於從點i到點j恰好經過2條邊的路徑數(列舉k為中轉點)。類似地,C*A的第i行第j列就表示從i到j經過3條邊的路徑數。同理,如果要求經過k步的路徑數,我們只需要二分求出A^k即可。

刷AC自動機的時候用到這種思想了才刷的這道題.

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<string>
#include<vector>
#include<queue>
#include<stack>
#include<cmath>
#include<set>
#include<map>
using namespace std;
#define rep(i,aa,n) for (int i=aa;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define oo cout<<"!!!"<<endl;
typedef long long ll;
typedef unsigned long long ull;
#define ms(s) memset(s, 0, sizeof(s))
const int inf = 0x3f3f3f3f;
//head




//HDU 2157.離散數學裡的一個結論.AC自動機那幾道題的解法來源於這道題

const int maxn = 111;
const int mod = 1000;
struct matrix
{
	int m[maxn][maxn];
};

matrix mul(matrix a,matrix b,int sz)
{
	matrix c;
	rep(i,0,sz)
		rep(j,0,sz)
		{
			c.m[i][j] = 0;
			rep(k,0,sz)
				c.m[i][j] = (c.m[i][j] + a.m[i][k]*b.m[k][j]) % mod;
		}

	return c;
}

matrix pow(matrix a,int n,int sz)
{
	matrix res;
	rep(i,0,sz)
		rep(j,0,sz)
			res.m[i][j] = i==j;
	while(n)
	{
		if(n & 1)res = mul(a,res,sz);
		a = mul(a,a,sz);
		n >>= 1;
	}
	return res;
}

int main() 
{
	int n,m,q;
	while(cin>>n>>m,n||m)
	{
		int x,y;
		matrix mat;
		ms(mat.m);
		rep(i,0,m)
			cin>>x>>y,mat.m[x][y]=1;
		cin>>q;
		int a,b,k;
		while(q--)
		{
			cin>>a>>b>>k;
			matrix ans = pow(mat,k,n);
			cout<<ans.m[a][b]<<endl;
		}
	}
    return 0;
}