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

Luogu P3758 可樂

Luogu P3758 可樂

本題是一個經典的矩陣快速冪好題。

但與其他題不一樣的是,這個題的資料範圍給的提示不是特別明顯。其他題,如 POJ3735 中,\(m\leq 10^{10}\),就是一個鮮明的提示。而此題中,\(t\leq 10^6\),不算典型舉證快速冪的資料範圍,但也卡住了普通矩陣連乘的時間複雜度。

從題面中他可以留在原地,或隨機前往一個相鄰的點,這個條件其實可以看出用的是矩陣快速冪(有點像 P6569 [NOI Online #3 提高組] 魔法值),問題就是如何處理自爆。

我們從問題的本質出發來思考這個問題。

自爆,其實就是打斷了他的雙腿,不能再走動了。對啊!我們可以設定一個點 \(n+1\)

,命名為自爆點,可樂到了這裡就要再也出不去了,也就是說,每個點都可以通向 \(n+1\),但是到了 \(n+1\) 這個點,就再也不能往外走了,即在 \(n+1\) 這個點形成了自環。

如果上述描述有點抽象,我們那樣例賴建一個矩陣,如下:

\[\begin{bmatrix}1&1&0&1\\1&1&1&1\\0&1&1&1\\0&0&0&1\end{bmatrix} \]

快速冪計算,最後將第一行的答案累加起來即可。記得取模

//Don't act like a loser.
//You can only use the code for studying or finding mistakes
//Or,you'll be punished by Sakyamuni!!!
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;

const int MAXN=40,mod=2017;

int n,m,t;

struct matrix {
	long long data[MAXN][MAXN];
	int col,row;
	
	void clear() {
		col=row=0;
		for(int i=0;i<MAXN;i++) {
			for(int j=0;j<MAXN;j++) {
				data[i][j]=0;
			}
		}
	}
	void initialize() {
		clear();
		col=row=n+1;
		for(int i=1;i<=n+1;i++) {
			data[i][i]=1;
		}
	}
	
	inline matrix operator *(matrix& b)const {
		matrix ret;
		ret.clear();
		
		for(int i=1;i<=col;i++) {
			for(int k=1;k<=row;k++) {
				if(data[i][k]==0) continue;
				for(int j=1;j<=b.row;j++) {
					ret.data[i][j]+=data[i][k]*b.data[k][j]%mod;
					ret.data[i][j]%=mod;
				}
			}
		}
		ret.col=col;
		ret.row=b.row;
		return ret;
	}
};

matrix ans;
matrix opt;

inline void qpow(int y) {
	while(y) {
		if(y&1) {
			ans=ans*opt;
		}
		opt=opt*opt;
		y>>=1;
	}
}

signed main() {
	cin>>n>>m;
	opt.initialize();
	for(int i=1;i<=n;i++) {
		opt.data[i][n+1]=1;
	} 
	for(int i=1;i<=m;i++) {
		int u,v;
		cin>>u>>v;
		opt.data[u][v]=opt.data[v][u]=1;
	}
	ans.initialize();
	opt.col=opt.row=n+1;
	cin>>t;
	qpow(t);
	int tot=0;
	for(int i=1;i<=n+1;i++) {
		tot=(tot+ans.data[1][i])%mod;
	}
	cout<<tot<<endl;
	return 0;
}