生成树计数
这两天阴差阳错的研究了邹等周冬前辈的两篇论文~~
理论
图的关联矩阵
对于无向图
G
,我们定义它的关联矩阵
举例说明:
对于此图
G
,其矩阵如下:
Kirchhoff 矩阵
为研究
B
的性质,我们需要考察一下
可以发现:
- i=j 时, BBTij=vi的度数
-
i≠j
时
- 如果存在 e(vi,vj) ,那么 BBTij=−1
- 否则 BBTij=0
BBTij 即为图的 Kirchhoff 矩阵
对于无向图
G
,它的 Kirchhoff 矩阵
Matrix-Tree 定理
本体
对于一个无向图
G
,他的生成树个数等于其
n−1
阶主子式:任取一个
r
,将
证明
以后再说……
应用
[bzoj1002]轮状病毒[FJOI2007]
Time Limit: 1 Sec Memory Limit: 162 MB
题目描述
轮状病毒有很多变种,所有轮状病毒的变种都是从一个轮状基产生的。一个 N 轮状基由圆环上 N 个不同的基原子和圆心处一个核原子构成的, 2 个原子之间的边表示这 2 个原子之间的信息通道。如下图所示:
N 轮状病毒的产生规律是在一个N轮状基中删去若干条边,使得各原子之间有唯一的信息通道,例如共有 16 个不同的 3 轮状病毒,如下图所示
现给定 N( N≤100 ),编程计算有多少个不同的 N 轮状病毒
输入格式
第一行有 1 个正整数 N
输出格式
计算出的不同的 N 轮状病毒数输出
样例输入
3
样例输出
16
这道题是一道很明显地在考察 Matrix-Tree 定理,但是我们不能直接用程序计算,这样会超时,我们可以应用此定理推出一个递推式:
然后本题并没有让我们取模输出,故要用到高精度。
其实还可以用矩阵快速幂再优化一下递推,不过没加就 AC 了十年前的省选还是简单啊
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN=105;
int n;
struct BN
{
int num[105],len;
BN(){memset(num,0,sizeof(num));len=-1;}
void adjust()
{
for(int i=0;i<=len;i++)
if(num[i]>9)
{
num[i+1]+=num[i]/10;num[i]%=10;
len=max(len,i+1);
}
for(int i=0;i<=len;i++)
if(num[i]<0)
{
num[i-1]-=(-num[i])/10;num[i]=10-((-num[i])%10);
}
while(!num[len]) len--;
}
BN operator * (int x)const
{
BN res=*this;
for(int i=0;i<=len;i++) res.num[i]*=x;
res.adjust();
return res;
}
BN operator + (int x)const
{
BN res=*this;
for(int i=0;i<=len||x;i++) res.num[i]+=x%10,x/=10;
res.adjust();
return res;
}
BN operator - (BN x)const
{
BN res;
for(int i=0;i<=max(len,x.len);i++) res.num[i]-=x.num[i];
res.adjust();
return res;
}
} f[MAXN];
int main()
{
int i;
scanf("%d",&n);
f[1]=BN()+1;f[2]=BN()+5;
for(i=3;i<=n;i++)
f[i]=f[i-1]*3-f[i-2]+2;
for(i=f[n].len;i>=0;i--) putchar(f[n].num[i]+'0');
printf("\n");
return 0;
}