说在前面
感觉现在打ACM容易没有想清楚就写,为了赶时间
还是该有一点OI的冷静思考
题目
题目大意
给出数字 N , K N,K N,K,求出满足以下条件的图有多少种
- 该图有 N N N个点
- 每个点恰好有一条入边,一条出边
- 不能出现长度大于K的环
有T组数据,每组数据满足
N
≤
1
0
6
,
N
2
≤
K
≤
1
0
9
N\leq 10^6 , \frac{N}{2} \le K\le10^9
N≤106,2N≤K≤109
且保证
∑
N
≤
1
0
7
\sum N \leq 10^7
∑N≤107
解法
首先,整个图是由若干个环(包括自环)构成的
正向做可以
Θ
(
N
2
)
\Theta(N^2)
Θ(N2)的dp
定义
f
[
i
]
f[i]
f[i]表示符合的图有多少种,转移
f
[
i
]
=
∑
(
f
[
i
−
j
]
∗
(
i
−
1
j
−
1
)
∗
(
j
−
1
)
!
)
f[i] = \sum\left(f[i-j]* \binom{i-1}{j-1}*(j-1)!\right)
f[i]=∑(f[i−j]∗(j−1i−1)∗(j−1)!),注意
j
≤
K
j\leq K
j≤K
但是没办法优化emmmm
发现数据范围有点意思,
N
2
≤
K
\frac{N}{2}\le K
2N≤K意味着,如果某个图不合法,那么它有且仅有一个环长度大于
K
K
K。计算方案的话,枚举这个不合法的环,剩下的部分随便构成"环图”,而
N
N
N个点构成的"环图"总共有
N
!
N!
N!个
所以补集和全集都很好算,这题就做完了
下面是代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
const int mmod = 1e9 + 7 , inv2 = 500000004 ;
int T , N , X ;
long long fac[1000005] , ifac[1000005] ;
long long s_pow( long long x , int b ){
long long rt = 1 ;
while( b ){
if( b&1 ) rt = rt * x %mmod ;
x = x * x %mmod , b >>= 1 ;
} return rt ;
}
void preWork(){
ifac[0] = fac[0] = 1 ;
for( int i = 1 ; i <= 1000000 ; i ++ ) fac[i] = fac[i-1] * i %mmod ;
ifac[1000000] = s_pow( fac[1000000] , mmod - 2 ) ;
for( int i = 999999 ; i ; i -- ) ifac[i] = ifac[i+1] * ( i + 1 )%mmod ;
}
long long Comb( int n , int m ){
return fac[n] * ifac[m] %mmod * ifac[n-m]%mmod ;
}
void solve(){
long long frac_up = 0 ;
for( int i = X + 1 ; i <= N ; i ++ ){
long long cnt = Comb( N , i ) * fac[i-1] %mmod * fac[N-i] %mmod ;
frac_up = ( frac_up + cnt ) %mmod ;
}
frac_up = ( fac[N] - frac_up + mmod )%mmod ;
long long ifrac_down = ifac[N] ;
printf( "%lld\n" , frac_up * ifrac_down %mmod ) ;
}
int main(){
scanf( "%d" , &T ) ;
preWork() ;
for( int i = 1 ; i <= T ; i ++ ){
scanf( "%d%d" , &N , &X ) ;
solve() ;
}
}