信息学奥数一本通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;
}
}

浙公网安备 33010602011771号