• Solution -「HDU 5498」Tree


    (mathcal{Description})

      link.
      给定一个 (n) 个结点 (m) 条边的无向图,(q) 次操作每次随机选出一条边。问 (q) 条边去重后构成生成树的方案总数,对 (p) 取模。

    (mathcal{Solution})

      首先求出 (n-1) 条边构成生成树的方案数,显然矩阵树定理。
      接着,令 (f(i,j)) 表示操作 (i) 次,去重后有 (j) 条边的方案数。那么有:

    [f(i,j)=jf(i-1,j)+(m-j+1)f(i-1,j-1) ]

      这个式子可以矩阵快速幂优化,最后把上面两个东西乘起来就是总方案啦。复杂度 (operatorname{O}(n^3log q))

    (mathcal{Code})

      这个 HDU 它一直 SF 呢 qwq。理论 AC 代码如下 w。

    #include <cstdio>
    #include <cstring>
    #include <assert.h>
    #include <iostream>
    
    const int MAXN = 100;
    int n, m, p, q, K[MAXN + 5][MAXN + 5];
    
    inline void add ( const int u, const int v ) {
    	++ K[u][u], ++ K[v][v], -- K[u][v], -- K[v][u];
    	if ( K[u][v] < 0 ) K[u][v] += p;
    	if ( K[v][u] < 0 ) K[v][u] += p;
    }
    
    inline int det ( const int n ) {
    	int ret = 1, swp = 1;
    	for ( int i = 1; i < n; ++ i ) {
    		for ( int j = i + 1; j < n; ++ j ) {
    			for ( ; K[j][i]; std::swap ( K[i], K[j] ), swp *= -1 ) {
    				int d = K[i][i] / K[j][i];
    				for ( int k = i; k < n; ++ k ) K[i][k] = ( K[i][k] - 1ll * d * K[j][k] % p + p ) % p;
    			}
    		}
    		if ( ! ( ret = 1ll * ret * K[i][i] % p ) ) return 0;
    	}
    	return ( ret * swp + p ) % p;
    }
    
    struct Matrix {
    	int n, m, mat[MAXN + 5][MAXN + 5];
    	Matrix () {} Matrix ( const int tn, const int tm ): n ( tn ), m ( tm ), mat {} {}
    	inline int* operator [] ( const int key ) { return mat[key]; }
    	inline Matrix operator * ( Matrix t ) {
    		assert ( m == t.n );
    		Matrix ret ( n, t.m );
    		for ( int i = 0; i <= n; ++ i ) {
    			for ( int k = 0; k <= m; ++ k ) {
    				for ( int j = 0; j <= t.m; ++ j ) {
    					ret[i][j] = ( ret[i][j] + 1ll * mat[i][k] * t[k][j] ) % p;
    				}
    			}
    		}
    		return ret;
    	}
    };
    
    inline Matrix qkpow ( Matrix A, int b ) {
    	Matrix ret ( A.n, A.m );
    	for ( int i = 0; i <= A.n; ++ i ) ret[i][i] = 1;
    	for ( ; b; A = A * A, b >>= 1 ) if ( b & 1 ) ret = ret * A;
    	return ret;
    }
    
    int main () {
    	int T;
    	for ( scanf ( "%d", &T ); T --; memset ( K, 0, sizeof K ) ) {
    		scanf ( "%d %d %d %d", &n, &m, &p, &q );
    		for ( int i = 1, u, v; i <= m; ++ i ) scanf ( "%d %d", &u, &v ), add ( u, v );
    		int tree = det ( n );
    		if ( ! tree || q < n - 1 ) { puts ( "0" ); continue; }
    		Matrix F ( 0, n - 1 ), T ( n - 1, n - 1 );
    		F[0][0] = 1;
    		for ( int i = 0; i < n; ++ i ) {
    			T[i][i] = i;
    			if ( i ) T[i - 1][i] = n - i;
    		}
    		F = F * qkpow ( T, q );
    		printf ( "%d
    ", int ( 1ll * tree * F[0][n - 1] % p ) );
    	}
    	return 0;
    }
    
  • 相关阅读:
    链表数据-PHP的实现
    关于go的init函数
    socket小计
    很随笔
    go获取当前项目下所有依赖包
    关于synergy的问题
    二叉树的最大路径和
    大数求和
    重载<<运算符第二个参数必须加上const
    表达式求值
  • 原文地址:https://www.cnblogs.com/rainybunny/p/13218674.html
Copyright © 2020-2023  润新知