洛谷 P2144 [FJOI2007]轮状病毒

题意简述

求一种特殊无向图的生成树个数
无向图如图:

题解思路

用Matrix-Tree定理,求出基尔霍夫矩阵来计算,再递推答案;
\(Ans(x)=3\times Ans(x-1)-Ans(x-2)+2\)

证明:

我们令轮状病毒圆心编号为1,圆环上点编号从2~n+1
则可以得出无向图的度数矩阵D和邻接矩阵E
\(D=\begin{bmatrix}n&0&0&0&0&\cdots&0&0&0&0\\0&3&0&0&0&\cdots&0&0&0&0\\0&0&3&0&0&\cdots&0&0&0&0\\0&0&0&3&0&\cdots&0&0&0&0\\0&0&0&0&3&\cdots&0&0&0&0\\\vdots&\vdots&\vdots&\vdots&\vdots&\ddots&\vdots&\vdots&\vdots&\vdots\\0&0&0&0&0&\cdots&3&0&0&0\\0&0&0&0&0&\cdots&0&3&0&0\\0&0&0&0&0&\cdots&0&0&3&0\\0&0&0&0&0&\cdots&0&0&0&3\\\end{bmatrix}\)
\(E=\begin{bmatrix}0&1&1&1&1&\cdots&1&1&1&1\\1&0&1&0&0&\cdots&0&0&0&1\\1&1&0&1&0&\cdots&0&0&0&0\\1&0&1&0&1&\cdots&0&0&0&0\\1&0&0&1&0&\cdots&0&0&0&0\\\vdots&\vdots&\vdots&\vdots&\vdots&\ddots&\vdots&\vdots&\vdots&\vdots\\1&0&0&0&0&\cdots&0&1&0&0\\1&0&0&0&0&\cdots&1&0&1&0\\1&0&0&0&0&\cdots&0&1&0&1\\1&1&0&0&0&\cdots&0&0&1&0\\\end{bmatrix}\)
再得到
基尔霍夫Kirchhoff矩阵 \(K=\begin{bmatrix}n&-1&-1&-1&-1&\cdots&-1&-1&-1&-1\\-1&3&-1&0&0&\cdots&0&0&0&-1\\-1&-1&3&-1&0&\cdots&0&0&0&0\\-1&0&-1&3&-1&\cdots&0&0&0&0\\-1&0&0&-1&3&\cdots&0&0&0&0\\\vdots&\vdots&\vdots&\vdots&\vdots&\ddots&\vdots&\vdots&\vdots&\vdots\\-1&0&0&0&0&\cdots&3&-1&0&0\\-1&0&0&0&0&\cdots&-1&3&-1&0\\-1&0&0&0&0&\cdots&0&-1&3&-1\\-1&-1&0&0&0&\cdots&0&0&-1&3\\\end{bmatrix}\)
然后我们要求K的代数余子式的值,显然是将带有n的那一行,那一列(即第一行,第一列)去掉
接下来就是求n阶行列式\(A=\begin{vmatrix}3&-1&0&0&\cdots&0&0&0&-1\\-1&3&-1&0&\cdots&0&0&0&0\\0&-1&3&-1&\cdots&0&0&0&0\\0&0&-1&3&\cdots&0&0&0&0\\\vdots&\vdots&\vdots&\vdots&\ddots&\vdots&\vdots&\vdots&\vdots\\0&0&0&0&\cdots&3&-1&0&0\\0&0&0&0&\cdots&-1&3&-1&0\\0&0&0&0&\cdots&0&-1&3&-1\\-1&0&0&0&\cdots&0&0&-1&3\\\end{vmatrix}\)
对于A,显然不能用\(n^3\)的高斯消元,所以我们要探究A的性质。
将A的第一行变换到最后一行,得到\(B=\begin{vmatrix}-1&3&-1&0&\cdots&0&0&0&0\\0&-1&3&-1&\cdots&0&0&0&0\\0&0&-1&3&\cdots&0&0&0&0\\0&0&0&-1&\cdots&0&0&0&0\\\vdots&\vdots&\vdots&\vdots&\ddots&\vdots&\vdots&\vdots&\vdots\\0&0&0&0&\cdots&-1&3&-1&0\\0&0&0&0&\cdots&0&-1&3&-1\\-1&0&0&0&\cdots&0&0&-1&3\\3&-1&0&0&\cdots&0&0&0&-1\\\end{vmatrix}\)
先根据行列式的性质得到\(A=(-1)^{n-1}\times B\)
我们发现除了左下角有三个数字外,B已经是一个上三角行列式,接下来是要消去左下角的三个数字
先消第n-1行(倒数第二行)
假设现在在消第i位,设\(F_i\)表示这一行的第i个数,\(G_i\)表示这一行第i+1个数
那么我们可以将B的第i行\(\times F_i\)加到这一来,就可消去第i位。因为第i行-1后面一定是3,所以第i+1位\(+3\times F_i\)同理,第i+2位\(+(-F_i)\)

\(\begin{cases}F_{i+1}=G_i+3\times F_i\\G_{i+1}=-F_i\\\end{cases}\)
由第二个式子推出\(G_i=-F_{i-1}\)带人第一个式子中,得到\(F_{i+1}=3\times F_i-F_{i-1}\),同时也可得到\(G_{i+1}=3\times G_i-G_{i-1}\)
然后可以快速地将第n-1行消成\(\begin{bmatrix}0&0&0&0&\cdots&0&0&F_{n-1}-1&G_{n-1}+3\end{bmatrix}\)
同样的方法,我们可以来消第n行
设第i位为\(H_i\),第i+1位为\(L_i\)
可得方程
\(\begin{cases}H_{i+1}=L_i+3\times H_i\\L_{i+1}=-H_i\\\end{cases}\)
解得\(H_{i+1}=3\times H_i-H_{i-1}\qquad L_{i+1}=3\times L_i-L_{i-1}\)
将第n行消成\(\begin{bmatrix}0&0&0&0&\cdots&0&0&H_{n-1}&L_{n-1}-1\end{bmatrix}\)
\(F_{n-1}-1=f\qquad G_{n-1}+3=g\qquad H_{n-1}=h\qquad L_{n-1}-1=l;\)
\(B=\begin{vmatrix}-1&3&-1&0&\cdots&0&0&0&0\\0&-1&3&-1&\cdots&0&0&0&0\\0&0&-1&3&\cdots&0&0&0&0\\0&0&0&-1&\cdots&0&0&0&0\\\vdots&\vdots&\vdots&\vdots&\ddots&\vdots&\vdots&\vdots&\vdots\\0&0&0&0&\cdots&-1&3&-1&0\\0&0&0&0&\cdots&0&-1&3&-1\\0&0&0&0&\cdots&0&0&f&g\\0&0&0&0&\cdots&0&0&h&l\\\end{vmatrix}\)=\((-1)^{n-2}\times \begin{vmatrix}f&g\\h&l\\\end{vmatrix}\)
所以\(A=(-1)^{n-1}\times B=(-1)^{2n-3}\times \begin{vmatrix}f&g\\h&l\\\end{vmatrix}=-f\times l+g\times h\)
再推一推F,G,H,L,可以看出
\(H_{i-1}=L_{i}=F_{i}=G_{i+1}\)
最后就可以推出\(Ans(n)=3\times Ans(n-1)-Ans(n-2)+2\)
如果推不出,也可以求出\(F_{i-1},F_{i},F_{i+1}\)再来求答案

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
int n;
struct Number {
	int len;
	int num[1000];
	Number() {
		len=0;
		memset(num,0,sizeof(num));
	}
	void operator =(const Number x) {
		len=x.len;
		for (register int i=1;i<=len;++i) num[i]=x.num[i];
	}
	void operator +=(const Number x) {
		len=std::max(len,x.len);
		for (register int i=1;i<=len;++i) {
			num[i]=num[i]+x.num[i];
			if (num[i]>=10) num[i]-=10,++num[i+1];
		}
		if (num[len+1]) ++len;
	}
	void operator -=(const Number x) {
		int xx=std::min(len,x.len);
		for (register int i=1;i<=xx;++i) {
			num[i]=num[i]-x.num[i];
			if (num[i]<0) num[i]+=10,--num[i+1];
		}
		while (!num[len]) --len;
	}
	Number operator *(const int& x) {
		Number res;
		for (register int i=1;i<=len;++i) {
			res.num[i]+=num[i]*3;
			res.num[i+1]+=res.num[i]/10;
			res.num[i]=res.num[i]%10;
		}
		res.len=len;
		if (res.num[len+1]) ++res.len;
		return res;
	}
	void print() {
		for (register int i=len;i;--i) printf("%d",num[i]);
		puts("");
	}
} x,y,z,_2;
int main() {
	scanf("%d",&n);
	if (n==1) puts("1");
	else if (n==2) puts("5");
	else {
		_2.len=1; _2.num[1]=2;
		x.len=x.num[1]=1;
		y.len=1; y.num[1]=5;
		for (register int i=3;i<=n;++i) {
			z=y*3; z-=x; z+=_2;
			x=y; y=z;
		}
		z.print();
	}
} 
posted @ 2019-08-07 18:21  xuyixuan  阅读(190)  评论(0编辑  收藏  举报