这是一道非常综合的题目:
给一些边,有q次操作,每次选出一条边,q次以后把重复的边删除。问有多少种取法能够使得最后的结果是一棵树。
方法:
1. 对于一棵树,相当于q次操作选中了n-1种边,因为对图的任意一个树的子图,选择的方案数是一样的
定义dp[i][j]表示第i次操作选中了j种边的方案数 那么 dp[i+1][j] = dp[i][j]*j+dp[i-1][j-1]*(n-1-j )
转移要么从选了j-1条边转移,或者从选了j条边转移。构造 n×n的矩阵,把系数加进去,然后用快速幂求解即可
2. 一个图能够构成树的情况数
转自:http://www.cnblogs.com/kuangbin/archive/2013/07/27/3219707.html
Matrix-Tree定理(Kirchhoff矩阵-树定理)。Matrix-Tree定理是解决生成树计数问题最有力的武器之一。它首先于1847年被Kirchhoff证明。在介绍定理之前,我们首先明确几个概念:
1、G的度数矩阵D[G]是一个n*n的矩阵,并且满足:当i≠j时,dij=0;当i=j时,dij等于vi的度数。
2、G的邻接矩阵A[G]也是一个n*n的矩阵, 并且满足:如果vi、vj之间有边直接相连,则aij=1,否则为0。
我们定义G的Kirchhoff矩阵(也称为拉普拉斯算子)C[G]为C[G]=D[G]-A[G],则Matrix-Tree定理可以描述为:G的所有不同的生成树的个数等于其Kirchhoff矩阵C[G]任何一个n-1阶主子式的行列式的绝对值。所谓n-1阶主子式,就是对于r(1≤r≤n),将C[G]的第r行、第r列同时去掉后得到的新矩阵,用Cr[G]表示。
那么问题就变成求矩阵行列式的问题了。之前用用寻找列主元的方法对模数是质数的题做过一次
改题的博客:http://blog.csdn.net/firenet1/article/details/38065929
这题要对合数取模,比较困难。
其实消元要考虑的问题就是,处理第A[i][i]位置的时候,如何把A[j][i] (j > i)的位置且不为0的行的第i列变成0.
考虑a = A[i][i], b = A[j][i]
没次假设abs(a) > abs(b) (其他情况的话可以交换)
然后令a = a - b*a/b
如此操作几次以后a,b中就会出现一个0,结束即可。
然后判断A[i][i]是不是0,是的话和第j行交换即可。
==============复杂度分析,A[i][i]不为0的时候,每次操作新的A[i][i] <= 旧的A[i][i],所以均摊下来的复杂度是比较低的。
这种消元方法的代码也很好写,比较简单-----------------(---666 不是我想到的,我是请教别人,然后他想出来的)
#include
#include
#include
#include
#include
using namespace std;
#define ll long long
#define maxn 101
int p;
struct Node{
int mat[maxn][maxn];
void init(){
memset(mat,0,sizeof(mat));
}
void one(int n){
init();
for(int i = 0;i < n; i++)
mat[i][i] = 1;
}
int Det(int n, int mod1){ //计算行列式对mod1取模
int ans = 1;
int *p1,*p2,mul;
for(int i = 0;i < n; i++){
for(int j = i+1;j < n; j++){
while(mat[i][i] != 0 && mat[j][i] != 0){
if(abs(mat[i][i]) > abs(mat[j][i]))
p1=mat[i],p2=mat[j],mul=mat[i][i]/mat[j][i];
else
p1=mat[j],p2=mat[i],mul=mat[j][i]/mat[i][i];
for(int l = i;l < n; l++)
p1[l] = (p1[l]-(ll)p2[l]*mul)%mod1;
}
if(mat[i][i] == 0){
ans = -ans;
for(int l = i;l < n;l++)
swap(mat[i][l],mat[j][l]);
}
}
ans = (ll)ans*mat[i][i] % mod1;
}
return (ans+mod1)%mod1;
}
};
int matrix[maxn][maxn];
void multi(Node& a,Node& b,int n){ //矩阵乘法----加判断以减小计算次数
for(int i = 0;i < n; i++)
for(int j = 0;j < n; j++)
matrix[i][j] = 0;
for(int i = 0;i < n; i++){
for(int j = 0;j < n; j++){
if(a.mat[i][j] == 0) continue;
for(int k = 0;k < n; k++){
if(b.mat[j][k] == 0) continue;
matrix[i][k] = (matrix[i][k]+(ll)a.mat[i][j]*b.mat[j][k])%p;
}
}
}
for(int i = 0;i < n; i++)
for(int j = 0;j < n; j++)
a.mat[i][j] = matrix[i][j];
}
int mp[maxn][maxn];
int main(){
int t,n,m,q,u,v;
Node a,b,c;
scanf("%d",&t);
while(t--){
scanf("%d%d%d%d",&n,&m,&p,&q);
c.init();
for(int i = 0;i < m; i++){
scanf("%d%d",&u,&v);
u--,v--;
c.mat[u][u]++;
c.mat[v][v]++;
c.mat[u][v] = c.mat[v][u] = -1;
}
if(q < n-1){
printf("0\n");
continue;
}
int ans = c.Det(n-1,p);
if(ans == 0){
printf("0\n");
continue;
}
a.init();
b.one(n);
for(int i = 1;i < n; i++)
a.mat[i][i] = i,a.mat[i][i-1] = n-i;
while(q){
if(q&1) multi(b,a,n);
multi(a,a,n);
q/=2;
}
ans = (ll)ans*b.mat[n-1][0]%p;
printf("%d\n",ans);
}
return 0;
}