hdu 5498 Tree 动态规划+快速矩阵幂+生成树计数+高斯消元
2015-10-06 13:51
337 查看
Tree
Time Limit: 16000/8000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 33 Accepted Submission(s): 13
Problem Description
soda has an undirected graph with n vertices
and m edges.
He is playing a game with q rounds.
In each round, he randomly chooses an edge from the graph. After qrounds,
he removes the duplicated edges. If the remaining edges form a tree, soda wins the game.
soda wants to know the number of different ways to choose edge in each round that will make him win the game.
As the answer will be very large, soda gives you another integer p.
You should print the answer modulo p.
Note that two ways are considered different if at least in one round soda chooses different edges.
Input
There are multiple test cases. The first line of input contains an integer T,
indicating the number of test cases. For each test case:
The first line contains four integers n,m,p,q (2≤n≤100,1≤m≤n(n−1)2,1≤p,q≤109).
Each of the following m lines
contains a pair of integers x and y,
that show that an edge exists between vertices x and y (1 \le x, y \le n, x \ne y).
For each pair of vertices there will be at most one edge between them, no edge connects a vertex to itself.
Output
For each test case, print the answer modulo p in
a single line.
Sample Input
2
6 6 23 10
6 3
6 4
5 1
2 5
1 4
5 4
6 5 23 5
5 6
4 6
3 1
5 1
1 2
Sample Output
16
5
Source
BestCoder Round #58 (div.1)
这是一道非常综合的题目:
给一些边,有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<iostream> #include<cstring> #include<algorithm> #include<cstdio> #include<math.h> 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; }
相关文章推荐
- 命令行快速技巧:如何定位一个文件
- jquery+CSS实现的多级竖向展开树形TRee菜单效果
- C++动态规划之最长公子序列实例
- C++动态规划之背包问题解决方法
- cmd tree命令 以树形格式罗列文件
- 推荐8款jQuery轻量级树形Tree插件
- tree 以树形格式罗列文件
- EasyUI Tree+Asp.net实现权限树或目录树导航的简单实例
- E3 tree 1.6在Firefox下显示问题的修复方法
- js树形控件脚本代码
- C#使用动态规划解决0-1背包问题实例分析
- swing中Tree与滚动条用法实例分析
- Spark Decision Tree
- 动态规划
- 带check的treeView
- C++ 动态规划
- extjs 4 treepanel locked and expand
- JQuery EasyUI 实现tree的右键菜单
- flex tree 的两个小技巧
- Tree.log