zoukankan      html  css  js  c++  java
  • 洛谷 P2144 [FJOI2007]轮状病毒

    题意简述

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

    题解思路

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

    证明:

    我们令轮状病毒圆心编号为1,圆环上点编号从2~n+1
    则可以得出无向图的度数矩阵D和邻接矩阵E
    (D=egin{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=egin{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=egin{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=egin{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=egin{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} imes B)
    我们发现除了左下角有三个数字外,B已经是一个上三角行列式,接下来是要消去左下角的三个数字
    先消第n-1行(倒数第二行)
    假设现在在消第i位,设(F_i)表示这一行的第i个数,(G_i)表示这一行第i+1个数
    那么我们可以将B的第i行( imes F_i)加到这一来,就可消去第i位。因为第i行-1后面一定是3,所以第i+1位(+3 imes F_i)同理,第i+2位(+(-F_i))

    (egin{cases}F_{i+1}=G_i+3 imes F_i\G_{i+1}=-F_i\end{cases})
    由第二个式子推出(G_i=-F_{i-1})带人第一个式子中,得到(F_{i+1}=3 imes F_i-F_{i-1}),同时也可得到(G_{i+1}=3 imes G_i-G_{i-1})
    然后可以快速地将第n-1行消成(egin{bmatrix}0&0&0&0&cdots&0&0&F_{n-1}-1&G_{n-1}+3end{bmatrix})
    同样的方法,我们可以来消第n行
    设第i位为(H_i),第i+1位为(L_i)
    可得方程
    (egin{cases}H_{i+1}=L_i+3 imes H_i\L_{i+1}=-H_i\end{cases})
    解得(H_{i+1}=3 imes H_i-H_{i-1}qquad L_{i+1}=3 imes L_i-L_{i-1})
    将第n行消成(egin{bmatrix}0&0&0&0&cdots&0&0&H_{n-1}&L_{n-1}-1end{bmatrix})
    (F_{n-1}-1=fqquad G_{n-1}+3=gqquad H_{n-1}=hqquad L_{n-1}-1=l;)
    (B=egin{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} imes egin{vmatrix}f&g\h&l\end{vmatrix})
    所以(A=(-1)^{n-1} imes B=(-1)^{2n-3} imes egin{vmatrix}f&g\h&l\end{vmatrix}=-f imes l+g imes h)
    再推一推F,G,H,L,可以看出
    (H_{i-1}=L_{i}=F_{i}=G_{i+1})
    最后就可以推出(Ans(n)=3 imes 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();
    	}
    } 
    
  • 相关阅读:
    Java实现 LeetCode 173 二叉搜索树迭代器
    PHP array_reverse() 函数
    PHP array_replace_recursive() 函数
    PHP array_replace() 函数
    PHP array_reduce() 函数
    PHP array_rand() 函数
    C# 通配符转正则
    win10 uwp 验证输入 自定义用户控件
    win10 uwp 验证输入 自定义用户控件
    win10 uwp 验证输入 自定义用户控件
  • 原文地址:https://www.cnblogs.com/xuyixuan/p/11317006.html
Copyright © 2011-2022 走看看