• bzoj1002[FJOI2007]轮状病毒


    题目链接


    本文计算部分有参考于https://www.cnblogs.com/Parry-PY/p/7731858.html

    需要指出的是,https://www.cnblogs.com/Parry-PY/p/7731858.html 之中,圈4[n-2]阶行列式的最后一个元素应该是(-1)而不是(0)


    问题分析

    这是一个生成树计数问题。我们使用Matrix-Tree定理。如果我们将点标号(0)(n),中间的那个为(0),我们就能得到这样的一个(n+1)阶方阵:

    [egin{bmatrix} n&-1&-1&-1&-1&cdots&-1&-1&-1\ -1&3&-1&0&0&cdots&0&0&-1\ -1&-1&3&-1&0&cdots&0&0&0\ -1&0&-1&3&-1&cdots&0&0&0\ vdots & vdots&vdots&vdots&vdots&vdots&vdots&vdots&vdots\ -1&0&0&0&0&cdots&-1&3&-1\ -1&-1&0&0&0&cdots&0&-1&3 end{bmatrix} ]

    而此题我们难以找到一个足够大的模数使高斯消元在模意义下运算来避免实数运算。而数字过大实数运算将产生巨大的误差。而此方阵极有特点,我们尝试算出较为简单的式子。

    根据Matrix_Tree定理,我们计算:

    [egin{vmatrix} 3&-1&0&0&cdots&0&0&-1\ -1&3&-1&0&cdots&0&0&0\ 0&-1&3&-1&cdots&0&0&0\ vdots&vdots&vdots&vdots&vdots&vdots&vdots&vdots\ 0&0&0&0&cdots&-1&3&-1\ -1&0&0&0&cdots&0&-1&3 end{vmatrix} ]

    我们记这个(n)阶行列式为(G_n)。同时,我们记下面这个(n)阶行列式为(F_n)

    [egin{vmatrix} 3&-1&0&0&cdots&0&0&0\ -1&3&-1&0&cdots&0&0&0\ 0&-1&3&-1&cdots&0&0&0\ vdots&vdots&vdots&vdots&vdots&vdots&vdots&vdots\ 0&0&0&0&cdots&-1&3&-1\ 0&0&0&0&cdots&0&-1&3 end{vmatrix} ]

    (G_n)按最后一行行展开:

    [G_n=(-1) imes(-1)^{n+1} egin{vmatrix} -1&0&0&cdots&0&0&-1\ 3&-1&0&cdots&0&0&0\ -1&3&-1&cdots&0&0&0\ vdots&vdots&vdots&vdots&vdots&vdots&vdots\ 0&0&0&cdots&-1&3&-1 end{vmatrix} +(-1) imes (-1)^{2n-1} egin{vmatrix} 3&-1&0&0&cdots&0&-1\ -1&3&-1&0&cdots&0&0\ 0&-1&3&-1&cdots&0&0\ vdots&vdots&vdots&vdots&vdots&vdots&vdots\ 0&0&0&0&cdots&-1&-1 end{vmatrix} +3 imes(-1)^{2n}F_{n-1} ]

    继续操作(前一个按第一行展开,后一个按最后一列展开):

    [egin{aligned} G_n=&3F_{n-1}+\ &(-1)^{n+2}igl( (-1) imes(-1)^{1+1} egin{vmatrix} -1&0&cdots&0&0&0\ 3&-1&cdots&0&0&0\ vdots&vdots&vdots&vdots&vdots&vdots\ 0&0&cdots&-1&3&-1 end{vmatrix} + (-1) imes(-1)^{n-1+1} imes F_{n-2} igr)+\ & igl( (-1) imes(-1)^{n-1+1} egin{vmatrix} -1&3&-1&0&cdots&0\ 0&-1&3&-1&cdots&0\ vdots&vdots&vdots&vdots&vdots&vdots\ 0&0&0&0&cdots&-1 end{vmatrix}+(-1) imes(-1)^{n-1+n-1}F_{n-2} igr) end{aligned}\ egin{aligned} G_n&=3F_{n-1}+(-1)^{n+2}(-1 imes(-1)^{n-2}+(-1)^{n+1}F_{n-2})+((-1)^{n+1} imes(-1)^{n-2}+(-1)^{2n-1}F_{n-2})\ &=3F_{n-1}-2F_{n-2}-2 end{aligned} ]

    (F_n)按第一行展开,然后按第一行展开:

    [egin{aligned} F_n&=3 imes(-1)^{1+1}F_{n-1}+(-1) imes(-1)^{1+2} egin{vmatrix} -1&-1&0&cdots&0&0&0\ 0&3&-1&cdots&0&0&0\ vdots&vdots&vdots&vdots&vdots&vdots&vdots\ 0&0&0&cdots&-1&3&-1\ 0&0&0&cdots&0&-1&3 end{vmatrix}\ &=3F_{n-1}+(-1) imes(-1)^{1+1}F_{n-2}+(-1) imes(-1)^{1+2} imes0\ &=3F_{n-1}-F_{n-2} end{aligned} ]

    所以,我们有了:

    [egin{aligned} G_n&=3F_{n-1}-2F_{n-2}-2\ F_n&=3F_{n-1}-F_{n-2} end{aligned} ]

    我们发现(F_n=G(n)+F_{n-2}+2),即(G_n=F_n-F_{n-2}-2)。所以到这里,实际上已经可以做了。但是我们还能简化:

    [egin{aligned} G_n&=3(G_{n-1}+F_{n-3}+2)-2F_{n-2}-2\ &=3G_{n-1}+3F_{n-3}-2F_{n-2}+4\ &=3G_{n-1}+3F_{n-3}-2(3F_{n-3}-F_{n-4})+4\ &=3G_{n-1}-3F_{n-3}+2F_{n-4}+4\ &=3G_{n-1}-G_{n-2}+2 end{aligned} ]

    到此大功告成。注意使用高精度。

    参考程序

    #include <bits/stdc++.h>
    using namespace std;
    
    struct node {
    	int A[ 110 ];
    	node operator * ( const int k ) const {
    		node Ans;
    		memset( Ans.A, 0, sizeof( Ans.A ) );
    		Ans.A[ 0 ] = A[ 0 ];
    		for( int i = 1; i <= A[ 0 ]; ++i ) Ans.A[ i ] = A[ i ] * k;
    		for( int i = 1; i <= A[ 0 ]; ++i ) {
    			Ans.A[ i + 1 ] += Ans.A[ i ] / 10;
    			Ans.A[ i ] %= 10;
    		}
    		while( Ans.A[ Ans.A[ 0 ] + 1 ] ) {
    			++Ans.A[ 0 ];
    			Ans.A[ Ans.A[ 0 ] + 1 ] += Ans.A[ Ans.A[ 0 ] ] / 10;
    			Ans.A[ Ans.A[ 0 ] ] %= 10;
    		}
    		return Ans;
    	}
    	node operator - ( const node Other ) const {
    		node Ans;
    		memset( Ans.A, 0, sizeof( Ans.A ) );
    		Ans.A[ 0 ] = A[ 0 ];
    		for( int i = 1; i <= A[ 0 ]; ++i ) {
    			if( Ans.A[ i ] + A[ i ] < Other.A[ i ] ) {
    				Ans.A[ i ] += 10;
    				Ans.A[ i + 1 ] -= 1;
    			}
    			Ans.A[ i ] += A[ i ] - Other.A[ i ];
    		}
    		while( Ans.A[ 0 ] > 1 && Ans.A[ Ans.A[ 0 ] ] == 0 ) --Ans.A[ 0 ];
    		return Ans;
    	}
    	node operator + ( const int k ) const {
    		node Ans;
    		memcpy( Ans.A, A, sizeof( Ans.A ) );
    		Ans.A[ 1 ] += k;
    		for( int i = 1; i <= Ans.A[ 0 ]; ++i ) {
    			Ans.A[ i + 1 ] += Ans.A[ i ] / 10;
    			Ans.A[ i ] %= 10;
    		}
    		while( Ans.A[ Ans.A[ 0 ] + 1 ] ) {
    			++Ans.A[ 0 ];
    			Ans.A[ Ans.A[ 0 ] + 1 ] += Ans.A[ Ans.A[ 0 ] ] / 10;
    			Ans.A[ Ans.A[ 0 ] ] %= 10;
    		}
    		return Ans;
    	}
    };
    node F[ 110 ];
    
    int main() {
    	F[ 1 ].A[ 1 ] = 1;
    	F[ 2 ].A[ 1 ] = 5;
    	F[ 1 ].A[ 0 ] = F[ 2 ].A[ 0 ] = 1;
    	int n;
    	scanf( "%d", &n );
    	for( int i = 3; i <= n; ++i )
    		F[ i ] = F[ i - 1 ] * 3 - F[ i - 2 ] + 2;
    	for( int i = F[ n ].A[ 0 ]; i >= 1; --i ) printf( "%d", F[ n ].A[ i ] ); printf( "
    " );
    	return 0;
    }
    
  • 相关阅读:
    pycharm中启动Django方法
    Python ——selenium报错 'chromedriver.exe' executable needs to be in PATH
    软件测试
    C#&.Net干货分享- 构建PrinterHelper直接调用打印机相关操作
    C#&.Net干货分享- iTextSharp导出数据源到PDF
    C#&.Net干货分享-构建Aocr_ImageHelper读取图片文字做解析
    C#&.Net干货分享-构建后台自动定时任务的源码
    SQL Server清理数据库日志的脚本-干货
    SQL Server通过函数把逗号分隔的字符串拆分成数据列表的脚本-干货
    SQL Server通过定义函数返回字段数据列表模板-干货
  • 原文地址:https://www.cnblogs.com/chy-2003/p/10398479.html
Copyright © 2020-2023  润新知