51nod 1806wangyurzee的树

Posted twilight-sx

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了51nod 1806wangyurzee的树相关的知识,希望对你有一定的参考价值。

  看这道题目懵逼了好久, (m <= 17) 一眼容斥,然而并没有想到怎么求出生成树的个数。然后灵光一闪——我是不学过一个叫Prüfer编码的东西嘛?!那就完美解决啦~

  Prüfer编码就是将一棵无根树映射到一串编码上的编码方法,一棵 (n) 个节点的树与一个长度为 (n - 2) 的编码串一一对应。所以我们要求合法的 = 总数 (n ^ {n - 2}) - 不合法的方案数。不合法的方案数 = 至少有 (1) 个不合法 - 至少有 (2) 个不合法 + 至少有(3) 个不合法……有何求出至少有 (k) 个不合法的方案数呢?

  我们可以首先搜索出这 (k) 个限制(复杂度约为 (2^{17})),然后令这(k) 个限制的 (sum = sum d[i] - 1),(sum) 即为这 (k) 个限制中所牵涉到的节点在数列中一共应该出现的次数。满足这个限制(每一个节点出现 (d[i] - 1) 次)的数列个数即为 (frac{sum!}{prod (d[i] - 1)!})。又因为这 (sum) 个数可以出现在长度为 (n - 2) 的数列中的任何位置,所以 乘上(C(n - 2, sum)),剩下的 (n - 2 - sum) 个数则可以随便选择,有 ((n - k) ^ {n - sum - 2}) 种方案。完美~

#include <bits/stdc++.h>
using namespace std;
#define maxn 2000000
#define int long long
#define mod 1000000007
int n, m, fac[maxn];
int cnt, tot = 1, Ans = 1, S[maxn];
bool mark[maxn], vis[maxn];

int read()
{
    int x = 0, k = 1;
    char c;
    c = getchar();
    while(c < 0 || c > 9) { if(c == -) k = -1; c = getchar(); }
    while(c >= 0 && c <= 9) x = x * 10 + c - 0, c = getchar();
    return x * k;
}

struct node
{
    int x, d;
}Q[maxn];

int Qpow(int x, int timer)
{
    int base = 1;
    for(; timer; timer >>= 1, x = x * x % mod)
        if(timer & 1) base = base * x % mod; 
    return base;
}

int C(int n, int m)
{ 
    if(m > n) return 0;
    return fac[n] * Qpow(fac[m], mod - 2) % mod * Qpow(fac[n - m], mod - 2) % mod; 
}

void Search(int now, int last)
{
    if(m - last + 1 < now) return;
    if(!now)
    {
        int tem = 1, sum = 0;
        for(int i = 1; i <= cnt; i ++)
            tem = tem * (fac[Q[S[i]].d - 1]) % mod,
            sum += Q[S[i]].d - 1;
        if(sum > n - 2) return;
        tem = Qpow(tem, mod - 2); 
        tem = tem * fac[sum] % mod * C(n - 2, sum) % mod * Qpow(n - cnt, n - sum - 2) % mod; 
        if(cnt & 1) Ans = (Ans - tem + mod) % mod;
        else Ans = (Ans + tem) % mod;
        return;
    }
    for(int i = last; i <= m; i ++)
    {
        if(vis[Q[i].x]) continue;
        S[++ cnt] = i, vis[Q[i].x] = 1; Search(now - 1, i + 1);
        cnt --, vis[Q[i].x] = 0;
    }
}

signed main()
{
    n = read(), m = read();
    for(int i = 1; i <= m; i ++)
        Q[i].x = read(), Q[i].d = read();
    if(n - 2 > 0) Ans = Qpow(n, n - 2);
    else
    {
        printf("1
");
        return 0;
    }
    fac[0] = 1;
    for(int i = 1; i <= n; i ++) fac[i] = fac[i - 1] * i % mod;
    for(int i = 1; i <= m; i ++) Search(i, 1);
    printf("%lld
", Ans);
    return 0;
}

 

以上是关于51nod 1806wangyurzee的树的主要内容,如果未能解决你的问题,请参考以下文章

51nod 1806wangyurzee的树

51nod1728

51Nod1868 彩色树 虚树

51nod 配对(求树的重心)

51NOD 1424 零树

51Nod2602 树的直径