可以写其他OJ题解了,赶紧来整一份
题目描述
询问有多少个包含 $n$ 个点,$m$ 条边的有向图,从 $1$ 号点到达 $n$ 号点需要经过至少 $n − 1$ 条边。
该有向图中可以包含重边和自环。
输入
输入共一行,包含两个整数 $n,m$。
输出
输出共一行,仅一个整数表示答案。
由于答案可能很大,输出结果请对 $10^9+7$ 取模。
数据范围
对于 $30\%$ 的数据,$n \leq 5$,$m \leq 10$;
对于 $60\%$ 的数据,$n \leq 80$,$m \leq 3000$;
对于 $100\%$ 的数据,$1 \leq n \leq 10000$,$1 \leq m \leq 50000$。
输入样例
2 2
输出样例
4
样例解释
共有以下 $4$ 种符合题意的有向图:
- $1 \rightarrow 2, 1 \rightarrow 2$
- $1 \rightarrow 2, 2 \rightarrow 1$
- $1 \rightarrow 2, 2 \rightarrow 2$
- $1 \rightarrow 2, 1 \rightarrow 1$
思路
首先,考虑爆搜
首先,找性质,从 1 号点到达 n 号点需要经过至少 n − 1 条边的图
等价于最短路径从一号点到 n 号点,将每个点遍历到且只遍历一次的图
证:
- 若最短路径中存在某个点没有遍历,那么必然存在至少一个点被遍历两次。
- 若最短路径中存在某个点被遍历两次及以上,则说明该路径存在环,与 其为最短路径 不符。
那么观察到这个性质之后,可以将 $n − 1$ 条边先分配给 $n$ 个点,形成一条链。
再考虑剩下 $m - n + 1$ 条边。
我们要将 $m - n + 1$ 条边加入链中,并且满足保证最短路不变。
直接算不好算,先算只加一条边。
根据容斥原理
合法的加入方案数 $=$ 总方案数 $−$ 不合法的方案数。
总方案数为 $n \times n = n ^ 2$,不符合法方案数即改变最短路长度的方案数,为 $C ^ 2 _ {n - 1} = \frac{(n - 1)(n - 2)}{2}$
那么我们就求出了只加一条边的合法方案数为 $n ^ 2 - \frac{(n - 1)(n - 2)}{2} = \frac{n ^ 2 + 3n - 2}{2}$。
接下来放剩下 $m - n + 1$ 条边,就可以看做一个非常经典的挡板问题
将 $A$ 个球放入 $B$ 个挡板中,方案数为 $C ^ A _ {A + B - 1}$
$m - n + 1$ 条边放入 $\frac{n ^ 2 + 3n - 2}{2}$ 个可选方案中,方案数为 $C ^ {m - n + 1} _ {m - n + 1 + \frac{n ^ 2 + 3n - 2}{2} - 1}$。
整理得 $C ^ {m - n + 1} _ {m + \frac{n(n + 1)}{2} - 1}$
由于要求从 $1$ 号点到 $n$ 号点,中间经过点顺序任意,所以还要乘上中间 $n - 2$ 个点的全排列方案数,即 $(n - 2)!$
最终答案为 $C ^ {m - n + 1} _ {m + \frac{n(n + 1)}{2} - 1}(n - 2)!$
需要取模,采用逆元即可。
时间复杂度分析
总时间复杂度瓶颈在求阶乘上。
由于求组合数时需要求出 $(m + \frac{n(n + 1)}{2} - 1)!$
所以总时间复杂度为平方级别。
具体时间复杂度较为玄学,不好分析。
看起来是 $O(n ^ 2 + m)$,但实际效果比这要好很多不然也过不去。
非常极限,需要常数上做一些优化。
C++ 代码
不优化代码 $80pts$
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
typedef long long LL;
const LL mod = 1000000007;
LL n, m;
LL fact(LL x)
{
LL ans = 1;
for (int i = 2; i <= x; i ++ )
ans = ans * i % mod;
return ans;
}
LL qmi(LL x,LL k)
{
LL ans = 1;
while (k)
{
if (k & 1) ans = ans * x % mod;
x = x * x % mod;
k >>= 1;
}
return ans;
}
int main()
{
cin >> n >> m;
LL k = (n * n + n >> 1) - 1;
LL inv = qmi(fact(k - 1 + n) * fact(m - n + 1) % mod, mod - 2);
cout << fact(n - 2) * fact(m + k) % mod * inv % mod;
return 0;
}
我居然中间还在试用一个长度为1e8的long long数组存阶乘。。空间炸了。。不愧是我。。
由于只需要用到几个数的阶乘,存一下就好啦~
优化后代码 $100pts$
#include <stdio.h>
typedef long long LL;
int n, m;
LL MOD = 1000000007;
LL power(LL x, LL k)
{
LL ans = 1;
while (k)
{
if (k & 1) ans = ans * x % MOD;
x = x * x % MOD;
k >>= 1;
}
return ans;
}
int main()
{
scanf("%d %d", &n, &m);
if (m < n - 1)
{
putchar('0');
return 0;
}
LL k = (n * n + n >> 1) - 1;
LL t1, t2, t3, t4;
t1 = t2 = t3 = t4 = 1;
for (int i = 1; i <= m + k; i ++ )
{
t4 = t4 * i % MOD;
if(i == k - 1 + n) t1 = t4;
if(i == m - n + 1) t2 = t4;
if(i == n - 2) t3 = t4;
}
LL inv = power(t1 * t2 % MOD, MOD - 2);
printf("%lld",t3 * t4 % MOD * inv % MOD);
return 0;
}
蒟蒻指出几点问题qwq
1.合法的加入方案数 = 总方案数 − 不合法的方案数本质是将原问题转化为容易求的可知集合的补集和容斥没有任何关系在本题中所代表的是$m−n+1$条边所合法的连边位置$u,v$然后从这些位置中安排连边
2.$n^2-\binom{n-1}{2}$本质上是把所有可能是非法的连边位置给减掉,而不是针对第一条边
3.对于插板法的叙述
插板法的本质是将同类的$A$个物品分给$B$个人或放进$B$个篮子内,将其问题转化为在$A-1$个位置中插$B-1$个挡板的问题,如果允许有人为$0$则多分$B$个物品再将$B$个物品抢走,其方案数为$C_{A+B-1}^{B-1}$
在本题中为将$m−n+1$条边分配到合法的连边位置的方案,因为允许重边,即可以有多个边在同一连边位置,且允许合法位置不存在边,所以可以用挡板法计算。
这里说下我个人的理解吧,若有错误的地方,还请您指出
先把包含于某集合中所有元素的数目先计算出来,然后再把计数时重复计算的数目排斥出去,这种 思想/方法 称为容斥原理
我并没有写
第一条边
,我写的是只加一条边
。设边的起点为 $u$,终点为 $v$,则 $u$ 一共有 $1 \sim n$ 这 $n$ 种取值,同理 $v$ 也只有 $n$ 种取值,所以边总共有 $n \times n = n ^ 2$ 种加的方法。
那么改变最短路长度的方案数即为 $u$ 在链上更靠 $1$ 的位置(相对于 $v$),$v$ 在链上更靠 $n$ 的位置(相对于 $u$),且 $u$ 到 $v$ 的距离大于 $1$,则这条边会缩短从 $1$ 到 $n$ 的距离(从 $1$ 走到 $u$ 时,向 $v$ 走,然后再从 $v$ 往 $n$ 走,距离小于 $n$)
首先对于插板法,您说在 $A - 1$ 个位置插入 $B - 1$ 个挡板,多分 $B$ 个物品再抢走
我将其理解为在 $A + B - 1$ 个位置插入 $B - 1$ 个挡板,方案数应为 $C _ {A + B - 1} ^ {B - 1}$
而您写的是 $C _ {A + B - 1} ^ B$,我比较菜,不太能理解,望您能细说下原因。
如果是 $C _ {A + B - 1} ^ {B - 1}$,那它等价于 $C _ {A + B - 1} ^ A$ 的,那么我写的是正确的
关于插板法的理解,有很多种,您这种理解方式是其一,但是在这里不是很好用于理解为什么放边的方案数是 $C ^ {m - n + 1} _ {m - n + 1 + \frac{n ^ 2 + 3n - 2}{2} - 1}$
我这里给出的理解方式,是直接将在 $\frac{n ^ 2 + 3n - 2}{2}$ 个可选方案中,放 $m - n + 1$ 条边看做是在 $\frac{n ^ 2 + 3n - 2}{2}$ 个“篮子”中,放 $m - n + 1$ 个物品,在本题中更便于理解
关于1.的话容斥原理您的理解也说了,是用于计数去重的。但关于合法的加入方案数 = 总方案数 − 不合法的方案数的本质还是将问题转化成补集,然后再考虑去重问题。
2.的话我觉得如果将叙述改成接下来所添加的边所连接的点$u,v$若在链上的路径长度大于等于2,这样添加的边就是不合法的,其等价于在$n-1$条边内任选两条边其第一条的起点$u$第二条的终点$v$,添加一条边$u,v$的方案数,能更好的解释为什么是$n^2-\binom{n-1}{2}$
关于3.窝是给出一种为什么是$\binom{A+B-1}{A}$而不是$\binom{A-1}{B}$的解释和补充没有考虑组合恒等的问题QAQ
对于3.窝觉得如果抽风巨巨补充一下为什么等价于放篮子的方案,为什么不是
$(m-n+1)\times(\frac{n^2+3n-2}{2}^{\underline{m-n+1}})$
,(可以有重边)放篮子为什么是$\binom{A+B-1}{A}$,可能会更好qwq
不过有重边还要考虑和链上边的重边的情况所以那个弱智式子就是个大概意思qwq(
而且我觉得在这没必要再解释隔板法了吧
您这句话我不是很理解,那个式子我也不是太能看懂。。抱歉wtcl/kk
*$\binom{A-1}{B-1}$qwq
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCOrz
qwq
巨佬啊%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
这题您应该直接秒了吧qwq
不啊我得做好几辈子/确信
%%%%%%%%%%%%%%%
快模
%%%%%%
%%%%%
%%%%%%大佬
您才是大佬啊Orz
%%%
%%%%