51Nod-1806-wangyurzee的树

235 篇文章 1 订阅
168 篇文章 0 订阅

ACM模版

描述

描述

题解

尝试了这个题,百度到需要用到一种我没有接触过的序列,叫做 prufer 序列,十分强大的一个工具,网上查查能查到很多关于这个的讲解,这里有一个比较重要的是,prufer 序列的每一种序列对应一种生成树,序列中每个结点出现的次数比每个结点在该生成树中的度数少一,然后就是组合容斥的问题了。

贴一下官方题解,和网上大神们讲的没有太大区别。

描述

这里注意要用到欧拉函数法求阶乘逆元,因为组合的公式里用得到它。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long ll;

const ll MOD = 1e9 + 7;
const int MAXN = 1e6 + 3;
const int MAXM = 20;

int n, m;
ll fac[MAXN];       //  阶乘
ll inv[MAXN];       //  阶乘的逆元
int u[MAXM];
int d[MAXM];
int vis[MAXM];

ll QPow(ll x, ll n)
{
    ll ret = 1;
    ll tmp = x % MOD;

    while (n)
    {
        if (n & 1)
        {
            ret = (ret * tmp) % MOD;
        }
        tmp = tmp * tmp % MOD;
        n >>= 1;
    }

    return ret;
}

void init()
{
    fac[0] = 1;
    for (int i = 1; i < MAXN; i++)
    {
        fac[i] = fac[i - 1] * i % MOD;
    }
    inv[MAXN - 1] = QPow(fac[MAXN - 1], MOD - 2);
    for (int i = MAXN - 2; i >= 0; i--)
    {
        inv[i] = inv[i + 1] * (i + 1) % MOD;
    }
}

template <class T>
inline void scan_d(T &ret)
{
    char c;
    ret = 0;
    while ((c = getchar()) < '0' || c > '9');
    while (c >= '0' && c <= '9')
    {
        ret = ret * 10 + (c - '0'), c = getchar();
    }
}

int main()
{
    init();

    scanf("%d%d", &n, &m);
    if (n == 1)
    {
        if (m == 1)
        {
            puts("0");
        }
        else
        {
            puts("1");
        }
        return 0;
    }

    for (int i = 0; i < m; i++)
    {
        scan_d(u[i]), scan_d(d[i]);
    }

    ll ans = QPow(n, n - 2);

    int state = (1 << m);
    for (int i = 1; i < state; i++)
    {
        int cnt = 0, sum = 0;
        memset(vis, 0, sizeof(vis));

        ll t = 1;
        for (int j = 0; j < m; j++)
        {
            if (i & (1 << j))
            {
                if (vis[u[j]])
                {
                    goto A;
                }
                cnt++;
                sum += (d[j] - 1);
                vis[u[j]] = 1;
                t = t * inv[d[j] - 1] % MOD;
            }
        }
        if (sum > n - 2)
        {
        A:
            continue;
        }
        ll tmp = fac[n - 2] * inv[n - 2 - sum] % MOD;
        tmp = tmp * QPow(n - cnt, n - 2 - sum) % MOD;
        tmp = tmp * t % MOD;
        if (cnt & 1)
        {
            ans = (ans - tmp) % MOD;
        }
        else
        {
            ans = (ans + tmp) % MOD;
        }
    }
    ans = (ans + MOD) % MOD;

    printf("%lld\n", ans);

    return 0;
}

参考

《求逆元》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值