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\)阶方阵:
\[\begin{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定理,我们计算:
\[\begin{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\)。
\[\begin{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)\times(-1)^{n+1}
\begin{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)\times (-1)^{2n-1}
\begin{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\times(-1)^{2n}F_{n-1}
\]
继续操作(前一个按第一行展开,后一个按最后一列展开):
\[\begin{aligned}
G_n=&3F_{n-1}+\\
&(-1)^{n+2}\bigl(
(-1)\times(-1)^{1+1}
\begin{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)\times(-1)^{n-1+1}\times F_{n-2}
\bigr)+\\
&
\bigl(
(-1)\times(-1)^{n-1+1}
\begin{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)\times(-1)^{n-1+n-1}F_{n-2}
\bigr)
\end{aligned}\\
\begin{aligned}
G_n&=3F_{n-1}+(-1)^{n+2}(-1\times(-1)^{n-2}+(-1)^{n+1}F_{n-2})+((-1)^{n+1}\times(-1)^{n-2}+(-1)^{2n-1}F_{n-2})\\
&=3F_{n-1}-2F_{n-2}-2
\end{aligned}
\]
将\(F_n\)按第一行展开,然后按第一行展开:
\[\begin{aligned}
F_n&=3\times(-1)^{1+1}F_{n-1}+(-1)\times(-1)^{1+2}
\begin{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)\times(-1)^{1+1}F_{n-2}+(-1)\times(-1)^{1+2}\times0\\
&=3F_{n-1}-F_{n-2}
\end{aligned}
\]
所以,我们有了:
\[\begin{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\)。所以到这里,实际上已经可以做了。但是我们还能简化:
\[\begin{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( "\n" );
return 0;
}