1. 程式人生 > 實用技巧 >P3758 TJOI2017 可樂

P3758 TJOI2017 可樂

記錄邊權的鄰接矩陣

\(D_{i,j}=+\infty(i=j)||i與j間邊權,i與j聯通,+\infty(i,j不連通)\)

\(D_{(i,j)}^2=min_{i\le k\le n}D_{i,k}+D_{k,j}\)

\(D^K_{i,j}表示i到j經過k條邊的最短路\)

記錄連通性的矩陣意義等價

\(A^k_{i,j}表示由i到j經過k條邊的方案數\)

P3758 TJOI2017 可樂

總方案數=\(t秒後處於各個位置的可能+所有已經爆炸的方案\)

在原先 n 個點的基礎上加一個點 0,表示機器人自爆後的去處,每個點都向 0 連一條單向邊

對於每個點可以新增一個自環,走這個自環就代表停在這裡,那麼每秒就都可以走一步

只需要建出鄰接矩陣,然後矩陣快速冪求出點 1 到每一點的方案數,求和即可

#include<cstdio>
#include<cstring>
#define mod 2017
#define int long long
using namespace std;
int answ = 0,n,m;
struct mat
{
    int a[101][101];
    init(){memset(a,0,sizeof(a));}
    void qwq(){for(int i=1;i<=n;i++)a[i][i]=1;}
}a,ans;
mat operator *(const mat &x,const mat &y)
    {
    mat z;
    for(int k=1;k<=n;++k)
    for(int i=1;i<=n;++i)
    for(int j=1;j<=n;++j)
    z.a[i][j]=(z.a[i][j]+x.a[i][k]*y.a[k][j]%mod)%mod;
    return z;
    }
signed main(){
	scanf("%lld%lld",&n,&m);
	n++;
	int x,y,t;
	for(int i = 1;i <= m;i++){
		scanf("%lld%lld",&x,&y);
		a.a[x][y] = 1; a.a[y][x] = 1;
	}
	for(int i=1;i<=n;i++)a.a[i][n]=1,a.a[i][i]=1;
	ans.init();
	scanf("%lld",&t);
	while(t){
		if(t & 1) ans = ans * a;
		t >>=1;
		a = a * a;
	}
	for(int i = 1;i <= n;i++)
	answ = (ans.a[1][i] + answ) % mod;
	printf("%lld",answ);
}