1. 程式人生 > 其它 >P3758 [TJOI2017]可樂

P3758 [TJOI2017]可樂

原題連結
考察:矩陣快速冪
思路:
  想了dp轉移方程,看了題解發現沒有這麼複雜,利用鄰接矩陣的性質就行了,參考Floyd演算法,外層每迴圈一次,相當於經過了一條邊,也是轉移到了一個新的狀態.
  把停留看成自環,城市之間的道路就等同於鄰接矩陣的原本意義,但是爆炸就比較難處理.大佬的思路是建立虛點,該點只有入度沒有出度.那麼每進行一次floyd就相當於進行狀態轉移.但是t太大了,所以進行矩陣加速.
  但是注意如果虛點0-->0 的權值為0的話,就會發現爆棧的方案數無法傳遞,所以賦值1.

Code

#include <iostream> 
#include <cstring>
using namespace std;
typedef long long LL;
const int N = 35,M = 2017;
int n,m,T,g[N][N],f[N]; 
void mul(int f[],int a[][N])
{
	int res[N] = {0};
	for(int i=0;i<N;i++)
	  for(int j=0;j<N;j++)
	   res[i] = (res[i]+(LL)f[j]*a[j][i])%M;
	memcpy(f,res,sizeof res);
}
void mul(int a[][N])
{
	int res[N][N] = {0};
	for(int i=0;i<N;i++)
	  for(int j=0;j<N;j++)
	    for(int k=0;k<N;k++)
	     res[i][j] = (res[i][j]+(LL)a[i][k]*a[k][j])%M;
	memcpy(a,res,sizeof res);
}
int main()
{
	scanf("%d%d",&n,&m);//爆炸的方案數需要傳遞 
	for(int i=0;i<=n;i++) g[i][0] = g[i][i] =1;
	while(m--) 
	{
		int x,y;
		scanf("%d%d",&x,&y);
		g[x][y] = g[y][x] = 1;
	}
	f[1] = 1;
	scanf("%d",&T);
	while(T)
	{
		if(T&1) mul(f,g);
		mul(g);
		T>>=1;
	}
	int ans = 0;
	for(int i=0;i<=n;i++) ans+=f[i],ans%=M;
	printf("%d\n",ans);
	return 0;
}