生成树计数

补充:度数矩阵,邻接矩阵,切尔霍夫矩阵

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/f

B=(-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;
}




  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值