信息学奥数一本通1813:图的计数

原题链接:https://ybt.ssoier.cn/ide1.php?pid=1813

【题目描述】
询问有多少个包含n个点,m条边的有向图,从1号点到达n号点需要经过至少(n−1)条边。该有向图中可以包含重边和自环。

【输入】
第一行两个整数n,m。

【输出】
仅一个整数表示答案\(mod10^9+7\)。

对于100%的数据,\(1≤n≤10000,1≤m≤50000\)。

题目只给了“从1号点到达n号点需要经过至少(n−1)条边”这一个条件,我们可以转换一下,我们观察这个,不难发现其实他是要每个点都经过一次的,因为假设有某一个点经过了两次,那么最短路径就可以直接把中间的去掉,就不是n-1了。为方便理解,我们把这个路径称为“主线”

那么有多少条不同的主线呢?因为1和n都是定的,所以有\((n-2)!\)个主线,假设我们主线是1->2->3->4->5

现在主线的n-1条边分完了,考虑剩下的m-n+1条边,定义为k,他们肯定是不能在主线上跳的,比如1->3或者2->4都不行,他们有三种情况:

第一种:是主线的重边,比如再来个1->2
第二种:是一条回边,比如2->1
第三种:自环,比如1->1

我在这里把回边和自环放到一起处理,那么就有\(n-1+n*(n+1)/2\)种情况,定义为q

那么我们就能把问题抽象出来了,剩下的额外边有多少种情况,就是有k个元素,分成q组,每组≥0,问有多少种情况,标准的隔板法,答案是\(C(k+q-1,q-1)\)个

于是我们就能得到答案:\((n-2)!*C(k-q-1,q-1)\)

But!我们会发现q会特别大,导致无法处理阶乘,那我们就转换一下,变成\(C(k-q-1,k)\),然后\(C(a,b)\)直接\(∏_{i=1}^{b}{(a-i+1)/i}\)算就行了

code:

#include <iostream>
#include <algorithm>
#include <cstring>
#include<algorithm>
#include<vector>
#define int long long
using namespace std;
const int mod = 1e9 + 7;

int n, m;
int fact[100005], inv[100005];

void init() {
    fact[0] = inv[0] = inv[1] = 1;
    for (int i = 1;i <= 100000;i++)fact[i] = fact[i - 1] * i % mod;
    for (int i = 2;i <= 100000;i++)inv[i] = (mod - mod / i) * inv[mod % i] % mod;
}

int C(int a, int b) {
    int ret = 1;
    for (int i = 1;i <= b;i++)ret = ret * (a - i + 1) % mod * inv[i] % mod;
    return ret;
}

signed main() {
    cin >> n >> m;
    init();
    if (m < n - 1)cout << 0;
    else if (m == n - 1)cout << fact[n - 2];
    else {
        int k = m - n + 1, q = ((n + 1) * n % mod * inv2 % mod + n - 1) % mod;
        cout << fact[n - 2] * C(k + q - 1, k) % mod;
    }
}
posted @ 2026-09-13 18:37  DK_orz  阅读(11)  评论(0)    收藏  举报