CodeForces - 917D Stranger Trees

题意:

给定一个有 n n n 个结点的树,结点带标,分别求与其有 k ( 0 ≤ k < n ) k(0 \leq k \lt n) k(0k<n) 条相同边的树的数量,答案模 1 0 9 + 7 10^9 + 7 109+7 ( n ≤ 100 ) (n \leq 100) (n100)

链接:

https://vjudge.net/problem/CodeForces-917D

解题思路:

也是个生成树计数的问题,但限制了 k k k 条边的出现,将给定的树边设为 x x x,那么再用矩阵树定理求得的方案数为关于 x x x n − 1 n - 1 n1 次多项式,对应 x x x 的幂次即为相同的边数。直接用多项式乘法、求逆解行列式不实际,那么枚举 n n n 个点,拉格朗日插值解出多项式系数即可。单次行列式求解 O ( n 3 l o g n ) O(n^3logn) O(n3logn),总复杂度 O ( n 4 l o g n ) O(n^4logn) O(n4logn)

参考代码:
#include<bits/stdc++.h>
 
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define sz(a) ((int)a.size())
#define pb push_back
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
const int maxn = 1e2 + 5;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;

ll qpow(ll a, ll b){

    ll ret = 1;
    while(b){

        if(b & 1) ret = ret * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ret;
}

struct Lag{

    ll xi[maxn], yi[maxn], a[maxn], lx[maxn], li[maxn], tp[maxn]; int n;
    void mul(ll f[], int n, ll b){

        for(int i = n; i > 0; --i) tp[i] = f[i], f[i] = f[i - 1];
        tp[0] = f[0], f[0] = 0;
        for(int i = 0; i <= n; ++i) f[i] = (f[i] + b * tp[i]) % mod;
    }
    void dev(ll f[], ll b, ll ret[]){

        for(int i = 0; i <= n; ++i) tp[i] = f[i];
        for(int i = n; i > 0; --i){

            ret[i - 1] = tp[i];
            tp[i - 1] = (tp[i - 1] - b * tp[i]) % mod;
        }
    }
    void solve(){

        for(int i = 0; i <= n; ++i) lx[i] = a[i] = 0;
        lx[0] = 1;
        for(int i = 1; i <= n; ++i) mul(lx, i, -xi[i]);
        for(int i = 1; i <= n; ++i){

            ll tmp = 1;
            for(int j = 1; j <= n; ++j){

                if(i == j) continue;
                tmp = tmp * (xi[i] - xi[j]) % mod;
            }
            tmp = yi[i] * qpow(tmp, mod - 2) % mod;
            dev(lx, -xi[i], li);
            for(int j = 0; j < n; ++j) a[j] = (a[j] + tmp * li[j]) % mod;
        }
        for(int i = 0; i < n; ++i) a[i] = (a[i] + mod) % mod;
    }
} lag;

struct MatTree{

    ll K[maxn][maxn]; int n;
    void init(int nn){

        n = nn;
        for(int i = 1; i <= n; ++i)
            for(int j = 1; j <= n; ++j) K[i][j] = 0;
    }
    void add(int u, int v, int w = 1, int t = 1){

        t ? (K[u][u] += w) %= mod : (K[v][v] += w) %= mod;
        (K[u][v] -= w) %= mod;
    }
    ll det(){

        ll ret = 1;
        for(int i = 1; i <= n; ++i){

            for(int j = i + 1; j <= n; ++j){

                while(K[j][i]){

                    ll t = K[i][i] / K[j][i];
                    for(int k = i; k <= n; ++k){

                        K[i][k] -= K[j][k] * t;
                        K[i][k] = (K[i][k] % mod + mod) % mod;
                        swap(K[i][k], K[j][k]);
                    }
                    ret = -ret;
                }
            }
            if(!K[i][i]) return 0;
            ret = ret * K[i][i] % mod;
        }
        return (ret + mod) % mod;
    }
    ll solve(int rt){

        for(int i = 1; i <= n; ++i) K[rt][i] = K[i][rt] = 0;
        K[rt][rt] = 1;
        return det();
    }
} mattr;

int G[maxn][maxn];
int n;

int main(){

    ios::sync_with_stdio(0); cin.tie(0);
    cin >> n;
    for(int i = 1; i < n; ++i){

        int u, v; cin >> u >> v;
        G[u][v] = G[v][u] = 1;
    }
    lag.n = n;
    for(int x = 0; x < n; ++x){

        mattr.init(n);
        for(int i = 1; i <= n; ++i){

            for(int j = 1; j <= n; ++j){

                mattr.add(i, j, G[i][j] ? x : 1);
            }
        }
        lag.xi[x + 1] = x;
        lag.yi[x + 1] = mattr.solve(1);
    }
    lag.solve();
    for(int i = 0; i < n; ++i){

        cout << lag.a[i] << " ";
    }
    cout << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值