补充:度数矩阵,邻接矩阵,切尔霍夫矩阵
http://www.cnblogs.com/noip/archive/2013/03/03/2941853.html
论文:http://wenku.baidu.com/view/0c086741be1e650e52ea990e.html
花个把小时看看,理解清楚.
然后简单概述一下,生成树计数的Matrix-Tree定理。
首先,一个图对应一个点和边的关联矩阵B。
每一列只有2个是1或-1,其余都是0,1或-1表示这条边的出入点。
将关联矩阵B转置,得到转置行列式BT(第i列变为第i行)
m m
B BT (i,j)=∑ Bi,k * BT k,j =∑ Bi,k * Bj,k
k=1 k=1
这样看,BBT成了B第i行和第j行的内积,然后我们计算一下,发现:
当i==j时,相乘之和就是所有出入度的绝对值之和,即点的度数
否则,i!=j时,如果i,j有一条边,乘起来是-1,其它无关的乘起来是0.
如果B是n×s的矩阵,BT就是s×n的矩阵
BBT就是n^2的矩阵。
n是点的个数
然后我们需要知道:
邻接矩阵,n*n的矩阵,如果i,j有边,矩阵的(i,j)为1,否则为0
度数矩阵,n*n的矩阵,主对角线上的点都是这个点i的度数,其余为0
考虑BBT和这两种矩阵的关系。
令C为BBT,D为度数矩阵,A为邻接矩阵, C=D-A.
我们就得到了这样一个定理(算是定理吧) 一张图的BBT矩阵就是这张图的度数矩阵-邻接矩阵。
而BBT就是kirchhoff矩阵。
由Matrix-Tree定理,一张图的最小生成树的个数等于它的kirchhoff矩阵的n-1阶主子式(行列式)的值
(大致证明是证明一棵树的kirchhoff矩阵的任意n-1阶行列式值为1,考察所有边集为n-1的图,如果是树,生成树个数+1.)
证明连接:http://www.bubuko.com/infodetail-899543.html
有了这个定理,我们就可以玩了。
然后,对于行列式补充几个知识:
1.行列式的值等于上三角矩阵的主对角线的乘积。
2.交换任意两行(列)行列式值取反。
3.行列式某一行或列乘上k,等于该行列式的值*k.
4.把行列式的某一行(列)乘上k,加到另一行(列)上,行列式值不变。
(PS:第2,4点是行列式转成三角矩阵的关键)
然后就可以做bzoj1002轮状病毒了
针对这道题,我们直接不考虑中间那个点(因为我们要求kirchhoff的n-1阶行列式,忽略中间那个点,求其他点搞成的kirchhoff行列式的值)
根据kirchhoff的定义,我们写出n轮病毒时的行列式。
3 -1 0 0 0 0 0 0 ``` 0 0 -1
-1 3 -1 0 0 0 0 0 ``` 0 0 0
0 -1 3 -1 0 0 0 0 ··· 0 0 0
0 0 -1 3 -1 0 0 0 ··· 0 0 0
0 0 0 -1 3 -1 0 0 ``` 0 0 0
```````````````````````````````````````````````
```````````````````````````````````````````````
```````````````````````````````````````````````
0 0 0 0 0 0 0 0 ``` -1 3 -1
-1 0 0 0 0 0 0 0 ``` 0 -1 3
行列式对角线都是点的度数,都为3,相邻编号(除了1和n相邻但编号不相邻)之间为-1,其余0
我们交换n-1次,把第一行转到最下面
-1 3 -1 0 0 0 0 0 ``` 0 0 0
0 -1 3 -1 0 0 0 0 ··· 0 0 0
0 0 -1 3 -1 0 0 0 ··· 0 0 0
0 0 0 -1 3 -1 0 0 ``` 0 0 0
```````````````````````````````````````````````
```````````````````````````````````````````````
```````````````````````````````````````````````
0 0 0 0 0 0 0 0 ``` -1 3 -1
-1 0 0 0 0 0 0 0 ``` 0 -1 3
3 -1 0 0 0 0 0 0 ``` 0 0 -1
观察一下就知道刚才那么做的用意了:转成上三角矩阵。
交换了n-1次,得到矩阵B,设原来的矩阵A,则A=B*(-1)^(n-1)
改为求矩阵B
只有最后两行最左边的部分不为0,我们就想办法把它搞成0.
先考虑倒数第2行:
我们先把第1行*(-1),与这一行相加,得到 0 -3 1 0 0 0 ··· 0 0 -1 3
然后是第2行*(-3),相加,得到0 0 -8 3 0 0 ``` 0 0 -1 3
然后一般化这种情况:
0 0 ``` F(K) G(K) ``` 0 0 -1 3
总会找到一行在F(K)的位置是-1,G(K)的位置是3,可以把这一行乘上F(K),并与当前行相加得到 0 0 ``` 0 3*F(K)+G(K) -F(K) ``` 0 0 -1 3
我们就得到了递推式F(K+1)=3*F(K)+G(K),G(K+1)=-F(K), F(1)=-1,F(2)=-3
一直到:
0 0 ``` F(n-3) G(n-3) -1 3
我们把倒数第4行乘上F(n-3)加上去 0 0 ``` 0 F(n-2) G(n-2)-1 3
然后是倒3 0 0 `` 0 0 F(n-1)-1 G(n-1)+3
我们设f=F(n-1)-1,g=G(n-1)+3,
倒数第二排就成为了 0 0 0 ``` 0 0 f g
当前矩阵:
-1 3 -1 0 0 0 0 0 ``` 0 0 0
0 -1 3 -1 0 0 0 0 ··· 0 0 0
0 0 -1 3 -1 0 0 0 ··· 0 0 0
0 0 0 -1 3 -1 0 0 ``` 0 0 0
```````````````````````````````````````````````
```````````````````````````````````````````````
```````````````````````````````````````````````
0 0 0 0 0 0 0 0 ``` -1 3 -1
0 0 0 0 0 0 0 0 ``` 0 f g
3 -1 0 0 0 0 0 0 ``` 0 0 -1
然后是考虑倒数第1排:
按先前一样的方法,只不过换一种定义.
定义H(K+1)=3*H(K)+I(K) I(K+1)=-H(K) H(1)=3 H(2)=8
到最后变成
0 0 0 ``` 0 0 H(n-1) I(n-1)-1
我们定义h=H(n-1),i=I(n-1)-1;
然后矩阵就变成了:
-1 3 -1 0 0 0 0 0 ``` 0 0 0
0 -1 3 -1 0 0 0 0 ··· 0 0 0
0 0 -1 3 -1 0 0 0 ··· 0 0 0
0 0 0 -1 3 -1 0 0 ``` 0 0 0
```````````````````````````````````````````````
```````````````````````````````````````````````
```````````````````````````````````````````````
0 0 0 0 0 0 0 0 ``` -1 3 -1
0 0 0 0 0 0 0 0 ``` 0 f g
0 0 0 0 0 0 0 0 ``` 0 h i
然后考虑最后一行最后一次怎么变
把倒数第2行乘上-h/f ,加上去: 0 0 0 0 0 ```` 0 0 i-gh/f
-1 3 -1 0 0 0 0 0 ``` 0 0 0
0 -1 3 -1 0 0 0 0 ··· 0 0 0
0 0 -1 3 -1 0 0 0 ··· 0 0 0
0 0 0 -1 3 -1 0 0 ``` 0 0 0
```````````````````````````````````````````````
```````````````````````````````````````````````
```````````````````````````````````````````````
0 0 0 0 0 0 0 0 ``` -1 3 -1
0 0 0 0 0 0 0 0 ``` 0 f g
0 0 0 0 0 0 0 0 ``` 0 0 i-gh/fB=(-1)^(n-2)*f*(i-gh/f)
那么A=(-1)^(2n-3)*f*(i-gh/f)=gh-fi
把g,h,f,i代入:
A=(G(n-1)+3)H(n-1)-(F(n-1)-1)(I(n-1)-1)= -H(n-1)F(n-2)+3*H(n-1)-(F(n-1)-1)(-H(n-2)-1)=-H(n-1)F(n-2)+3*H(n-1)+F(n-1)H(n-2)+F(n-1)-H(n-2)-1
=F(n-1)+3*H(n-1)-H(n-2)-1+F(n-1)H(n-2)-H(n-1)F(n-2)
=F(n-1)+H(n)-1+|F(n-1) H(n-1)|
|F(n-2) H(n-2)|
化成这个样纸了,继续
后面这个二阶行列式肿么搞?
|F(n-1) H(n-1)| = -|F(n-1) H(n-1)| = -|F(n-1) H(n-1)| = -|F(n-1) H(n-1)| = |F(n) H(n)|
|F(n-2) H(n-2)| |-F(n-2) -H(n-2)| |3*F(n-1)-F(n-2) 3*H(n-1)-H(n-2)| |F(n) H(n)| |F(n-1) H(n-1)|
所以搞成这个样子,就发现这个二阶行列式其实是一个常量:|F(2) H(2)| =-1
|F(1) H(1)|
A=F(n-1)+H(n)-2
然后这个式子清爽了许多。
设R(n)=F(n-1)+H(n)-2 3*R(n-1)-R(n-2)=3*(F(n-2)+H(n-1)-2)-F(n-3)-H(n-2)+2=3*F(n-2)-F(n-3)+3*H(n-1)-H(n-2)-4=F(n-1)+H(n)-4=R(n)-2
所以R(n)=3*R(n-1)-R(n-2)+2 R(1)=1,R(2)=5
然后没有然后了,递推式都给你了,还想肿么滴?
高精度?貌似必须要啊!
你可以线性递推,也可以转成矩阵乘法。
还是线性递推吧,搞一个高精度版!!
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#define base 10000
#define maxlen 101
#define get(x) (x-'0')
using namespace std;
/*
不考虑中间点,求出其他点组成的kirchhoff矩阵的值
*/
struct bign
{
int sign,len,c[maxlen];
bign()
{
sign=0;
len=1;
memset(c,0,sizeof(c));
}
void zero()
{
while(len-1&&!c[len])len--;
}
void writen(char *s)
{
int l=strlen(s),k=1,lim=0;
if(s[0]=='-')
{
sign=1;
lim=1;
}
for(int i=l-1;i>=lim;i--)
{
c[len]+=get(s[i])*k;
k*=10;
if(k==base)
{
k=1;
len++;
}
}
}
void Read()
{
char s[maxlen*base];
scanf("%s",s);
writen(s);
}
void Printf()
{
if(sign)printf("-");
printf("%d",c[len]);
for(int i=len-1;i>=1;i--)printf("%04d",c[i]);
printf("\n");
}
bool operator <(const bign &b)
{
if(len!=b.len)return len<b.len;
for(int i=len;i>=1;i--)
{
if(c[i]!=b.c[i])return c[i]<b.c[i];
}
return false;
}
bign operator = (const int &a)
{
char s[maxlen*base];
sprintf(s,"%d",a);
writen(s);
return *this;
}
bign operator + (const bign &b)
{
bign r;
r.len=max(len,b.len)+1;
for(int i=1;i<=r.len;i++)
{
r.c[i]=c[i]+b.c[i];
r.c[i+1]+=r.c[i]/base;
r.c[i]%=base;
}
r.zero();
return r;
}
bign operator + (const int &a)
{
bign b;
b=a;
return *this+b;
}
bign operator * (const bign &b)
{
bign r;
r.len=len+b.len+2;
for(int i=1;i<=len;i++)
{
for(int j=1;j<=b.len;j++)
{
r.c[i+j-1]+=c[i]*b.c[j];
r.c[i+j]+=r.c[i+j-1]/base;
r.c[i+j-1]%=base;
}
}
r.zero();
return r;
}
bign operator * (const int &a)
{
bign b;
b=a;
return *this*b;
}
bign operator - (const bign &b)
{
bign a,c;
a=*this,c=b;
if(a<c)
{
swap(a,c);
a.sign=1;
}
for(int i=1;i<=len;i++)
{
a.c[i]-=c.c[i];
if(a.c[i]<0)
{
a.c[i]+=base;
a.c[i+1]--;
}
}
a.zero();
return a;
}
}f[105];
int n;
int main()
{
scanf("%d",&n);
f[1]=1;
f[2]=5;
for(int i=3;i<=n;i++)f[i]=f[i-1]*3-f[i-2]+2;
f[n].Printf();
return 0;
}