听说有人想看轮状病毒的题解,所以重发下这篇原发于2011-10-06的网易博客,并把题解里面的一部分纯文本公式用csdn的公式编辑器重写了一遍,但保留了原来的橙色配色233
有趣的是这篇题解是在2011年发的,然后一年半之后我嫌我之前写得太口胡,所以就又加了详细的证明。
感觉我搞 OI 的时候一直觉得行列式非常神秘,无法理解。最初是从这道轮状病毒题知道行列式的吧,然后看的教程说行列式是从下文提到的那几个初等行列变换定义出来的,内心受到了极大的震撼。。因为当时幼小的我完全接受不了这么抽象东西。。
当然后来我知道了还可以用什么置换什么逆序对来定义,在高维空间中还对应了体积。。高中时又去看了《线性代数应该这样学》,觉得真是一本好书!可是还是接受不了这个抽象的行列式。。
我应该是初二的时候做的轮状病毒,然后中学时代行列式就一直像个幽灵一样,时不时地在做题或者看书时蹦出来,困扰着我。
后来上了大学吧,我记得很清楚,我大学第一堂课就是讲行列式的。那是星期一早上的第一节课,教室坐得满满的,我抢到了第一排的位置,像中学时代一样认真记笔记。老师讲课很有激情,讲得很仔细。但讲到行列式的时候,他非常生硬地说:数学家在研究中发现这个量反复出现,所以就定义出来了。然而这种解释我早就听说过了,所以我一直很不服气。
再后来吧,大学四年间我接触了大量抽象的数学概念和计算机概念,还有大量脑回路新奇的证明。大学的讲课速度跟高中完全不是一个量级,短短一学期的课就能把你从完全不懂的小白带到这个学科的前沿。我越来越发现,似乎每个学科都是从一些非常经典的问题和非常生动的例子出发,不断深究背后的矛盾,发现那些不直观的本质,逐渐升华,最后总是能用一大堆抽象的概念给我重塑一遍看待相关问题的世界观,让我拥有一种全新的“直观”。
所以现在我回过头来反而无法理解当初为什么那么纠结于行列式了。也许数学和科学的目的之一就是发现那些不直观的东西吧。要是数学概念都非常直观非常显然,那么学数学还有什么用呢?
Description
轮状病毒有很多变种,所有轮状病毒的变种都是从一个轮状基产生的。一个N轮状基由圆环上N个不同的基原子和圆心处一个核原子构成的,2个原子之间的边表示这2个原子之间的信息通道。如下图所示
![](https://i-blog.csdnimg.cn/blog_migrate/b97a9034c42b8a527cbf459315233c80.png)
N轮状病毒的产生规律是在一个N轮状基中删去若干条边,使得各原子之间有唯一的信息通道,例如共有16个不同的3轮状病毒,如下图所示
![](https://i-blog.csdnimg.cn/blog_migrate/06f505fffcb665c99a1d8f4e2fd4e8fd.png)
给定n(N<=100),编程计算有多少个不同的n轮状病毒。
Input
第一行有1个正整数n。
Output
将编程计算出的不同的n轮状病毒数输出
Sample Input
3
Sample Output
16
其实如果你仔细观察矩阵,可以发现它是这样的:(消去了0号点,就是病毒中间的那个圆坨坨)
下面就简单了。设F(n)为n轮状病毒的个数,然后自己手算一下上面的矩阵,
可以发现,F(n) = 3F(n - 1) - F(n - 2) + 2。
由此可以得到O(n)的算法。
我无聊,用矩阵乘法+快速幂进行优化……O(logn)
=============================================
update:
吐槽:你说是O(logn)就是O(logn)?敢不敢把高精度计算的时间也算上啊!慢得要死啊!自己弱就不要写题解啊!我当年是sb吗!!!
同学们,只要写高精度加减暴力递推就行了~
我不知道我上面这种含糊不清的题解导致了多少人晕晕地混过去了,深表歉意。时隔一年半,我把详细证明补上。
/*
这一段神犇无视啊……
来看这篇题解的同学八成是跟我当年一样是才开始做衡阳八中的新手。
这道题新手可以拿来练练高精度。
估计也没人喜欢看证明的吧。但是对于新手还是建议去看看基尔霍夫矩阵。
*/
有公式恐惧症的同学,治病的时候到了!!
首先行列式有很多性质,第a行 * k 加到 第b行上去,行列式的值不变。
三角行列式的值等于对角线元素之积。
第a行与第b行互换,行列式的值取反。
常数 * 行列式,可以把常数乘到某一行里去。
如果你行列式不是很熟~建议先google行列式~不然下面会看晕~
那么我们现在对行列式进行变换,我们把第一行与第二行交换,再把第二行与第三行交换...,再把第n - 1行与第n行变换。这些都是行列式初等行列变换,得到新的行列式:
这个行列式跟一开始的那个行列式的值不一定相等。
因为我们是通过n - 1次交换行的操作得到的,
为了说话方便我们称一开是的那个行列式为A,上面我刚写的是B
那么由行列式性质得:A = (-1)^(n - 1) * B
-1好烦人对吧……
好,那么现在可以正大光明地处理B了~
利用行列式性质,来手算这个行列式。之所以刚才有那么一步,就是为了方便手算。
因为观察B,发现就只剩下左下角的-1、3、-1三个倒霉了。
首先看倒数第二行:
用第一行的:
乘以-1来消,得:
然后再用第二行的:
乘以-3来消,得:
这样就有了初步感觉了~
现在把这个过程一般化,假设现在正在处理的这一行的第k个和第k+1个时是这样的:
总能找到上面的某一行:
乘以F(k)来消,根据我们设的数,应该得到的是:
于是得到:
F(k + 1) = G(k) + 3 * F(k)
G(k + 1) = -F(k)
整合一下:
F(k + 1) = 3 * F(k) - F(k - 1)
从初始的行和消了一次之后的行中取得边界条件:F(1) = -1,F(2) = -3
经过了漫长的消来消去,我们最终可以把倒数第二行变为:
然后用倒数第四行:
乘以F(n - 3)来消:
还剩下一步……用倒数第三行:
乘以F(n - 2)来消:
好现在搞定了倒数第二行,来看看成果:(设f = F(n - 1) - 1, g = G(n - 1) + 3)
好,现在来搞倒数第一行。
和倒数第二行的方法是类似的。
再设函数H(k)和I(k),意义与F(k)、G(k)类似,得:
H(k + 1) = I(k) + 3 * H(k)
I(k + 1) = -H(k)
其实跟F、G的递推式是一样的我会乱说?
H(k + 1) = 3 * H(k) - H(k - 1)
边界条件是:H(1) = 3,H(2) = 8
最后使劲搞一搞,倒数第一行就成了:
再来看成果:(设h = H(n - 1), i = I(n - 1) - 1)
f是不会为0的吧……
那么用倒数第二行来消倒数第一行,得:
现在这个行列式已经是三角行列式了。
那么它的值就是对角线元素之积。
于是乎:B = (-1) * (-1) * (-1) * ... * (-1) * f * (i - g * (h / f))
一共有n - 2个-1
那么如前文所述:A = (-1)^(n - 1) * B
又因为:B = (-1)^(n - 2) * (f * i - g * h)
于是有:A = (-1)^(2n - 3) * (f * i - g * h) = -f * i + g * h
来来……f、g、h、i的值带入:
把H、I的值带入:
然后再展开咯。。回忆下F、H的递推式~
那啥那个长得丑丑的占了两行的表示的是二阶行列式。
发现不能化简了?
没关系!
在行列式上动动手脚吧!
【FH定理】
对于任意大于2的k有:
【FH定理的证明】
对于行列式:
把行列式最下面的行取反,则行列式的值取反:
把行列式的上面的行乘以3加到下面去:
特意构造的递推式出现了:
有点眉目了~把第一行与第二行调换位置,行列式的值取反:
一目了然,这是k++后的行列式的样子。(pascal同学早日转C++)
那么立即推出:
FH定理得证。
利用FH定理,把F(1) = -1,F(2) = -3, H(1) = 3, H(2) = 8带入:
于是就爽了嘛!
化简:
进一步我们发现……
设
那么立即有:
所以,得到如下结论:
【轮状病毒定理】
轮状病毒的方案数满足递推式F(n) = 3 * F(n - 1) - F(n - 2) + 2,其中F(1) = 1, F(2) = 5
目前这题的这个我只会这么证。
肯定是有更简单方法证……这样证肯定不是官方解法……
其实这种题还是打表找规律吧汗。
但是这种题要是出现在了考试中,我会写状压DP。记录0、1、n - 1三个点间的连边状态,暴力DP一下。应该是可捉的。这才是正常思路吧囧。BZOJ的discuss里面有人发了类似的非数学方法。
=============================================
代码如下:(小心观看)
(matrix类和高精度的类我就不写了,摆不下)
int main()
{
freopen("1002.in", "r", stdin);
freopen("1002.out", "w", stdout);
int n;
cin >> n;
if (n == 1)
cout << 1 << endl;
else if (n == 2)
cout << 5 << endl;
else
{
matrix t(3, -1, 2,
1, 0 , 0,
0, 0 , 1
);
matrix res = cf(t, n - 2);
cout << (res.m[0][0] * 5 + res.m[0][1] + res.m[0][2]) << endl;
}
return 0;
}