[ICPC2019 沈阳网络赛]-J.Ghh Matin-补集转化

说在前面

感觉现在打ACM容易没有想清楚就写,为了赶时间
还是该有一点OI的冷静思考


题目

计蒜客T41404传送门

题目大意

给出数字 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 N106,2NK109
且保证 ∑ N ≤ 1 0 7 \sum N \leq 10^7 N107


解法

首先,整个图是由若干个环(包括自环)构成的
正向做可以 Θ ( 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[ij](j1i1)(j1)!),注意 j ≤ K j\leq K jK
但是没办法优化emmmm

发现数据范围有点意思, N 2 ≤ K \frac{N}{2}\le K 2NK意味着,如果某个图不合法,那么它有且仅有一个环长度大于 K K K。计算方案的话,枚举这个不合法的环,剩下的部分随便构成"环图”,而 N N N个点构成的"环图"总共有 N ! N! N!
所以补集和全集都很好算,这题就做完了


下面是代码

#include 
#include 
#include 
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() ;
	}
}

你可能感兴趣的:(杂题(乱搞))