codeforces893E 2000分组合数学

题目传送门

题意:

一个正整数x,把x分解为y个整数,有多少种方案。

相同的数字,顺序不同,认为方案不同。

数据范围: \dpi{150} 1 \leqslant n,m \leqslant 10^6 。

题解:

由唯一分解定理知:x = p_1^{c_1} * p_2^{c_2} * ... * p_{k}^{c_{k}}。 。其中 p_i 是质数。

把x分解成y个整数等价于把每种质因子 p_i 分成y组,允许空组出现。

假设答案是 ans ,那么每种质因子对答案的贡献是 ans = ans\;*\;C_{c_i + y - 1}^{y-1} 。

解释 C_{c_i + y - 1}^{y-1} :把 c_i 个质数分成 y 组,允许空组,那就相当于 c_i + y 个质数分为 y 组 ,不允许空组,因为你可以把每组减去1,那就出现了空组,所以是等价的。用隔板法得出该式。

由于负数的存在,并且x是正整数,所以y个数中有偶数个负数,最后ans = ans \;* 2^{y-1} 。

计算组合数时预处理出阶乘逆元会更快。每次都计算所需阶乘逆元是比较慢的。

感受:

学会了隔板法。

代码:

#include<bits/stdc++.h>
using namespace std ;
typedef long long ll ;
const int maxn = 105 ;
const int maxm = 1005 ;
const ll mod = 1e9 + 7 ;
ll fac[2000005] ;
ll inv[2000005] ;
ll qpow(ll a , ll b)
{
   ll ans = 1 ; 
   while(b)
   {
     if(b & 1)  ans = (ans * a) % mod ;
     b >>= 1 , a = (a * a) % mod ;
   }
   return ans % mod ;
}
void init() 
{
   fac[0] = fac[1] = 1 ;
   for(int i = 2 ; i <= 2e6 ; i ++)
     fac[i] = (fac[i - 1] * i) % mod ;	
   inv[2000000] = qpow(fac[2000000] , mod - 2) ;
   for(int i = 1999999 ; i >= 0 ; i --)
     inv[i] = inv[i + 1] * (i + 1) % mod ;
}
int cnt = 0 ;
bool vis[maxm] ;
int prime[maxm] ;
void get_prime()
{
   memset(vis , 0 , sizeof(vis)) ;
   vis[1] = 1 ;
   for(int i = 2 ; i <= maxm - 5 ; i ++)
   {
     if(!vis[i]) 
     prime[++ cnt] = i ;
     for(int j = 1 ; j <= cnt && i * prime[j] <= maxm - 5 ; j ++)
     {
       vis[i * prime[j]] = 1 ;
       if(i % prime[j] == 0) break ;
     }
   }
}
int num = 0 ;
int p[maxn] , c[maxn] ;
void init(int x)
{
   num = 0 ;	
   for(int i = 1 ; i <= cnt && prime[i] * prime[i] <= x ; i ++)
   {
   	 if(x % prime[i] == 0)
   	 {
   	   p[++ num] = prime[i] , c[num] = 0 ;
   	   while(x % prime[i] == 0)
       {
         c[num] ++ ;
         x /= prime[i] ;
       }	
	 }
   }
   if(x > 1)  p[++ num] = x , c[num] = 1 ;
}
ll C(int n , int m)
{
	return fac[n] * inv[m] % mod * inv[n - m] % mod ;
}
int main()
{
	int q ;
	scanf("%d" , &q) ;
	get_prime() ;
	init() ;
	while(q --)
	{
		int x , y ;
		ll ans = 1ll ;
		scanf("%d%d" , &x , &y) ;
		init(x) ;
		for(int i = 1 ; i <= num ; i ++) 
		  ans *= C(c[i] + y - 1 , y - 1) , ans %= mod ;
		ans *= qpow(2 , y - 1) , ans %= mod ;
		printf("%lld\n" ,ans) ;
	}
	return 0 ;
}

发布了215 篇原创文章 · 获赞 12 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Irving0323/article/details/104145482
今日推荐