传送门:【HDU】4658 Integer Partition
题目分析:
用了五边形数定理以及生成函数,然而我看懂了生成函数怎么搞这题却不知道为啥生成函数是五边形数形式= =
首先观察下面的图片:
很容易我们可以发现用这种方式构造N个五边形(假设一个点也算一个五边形),需要点的个数为:
接下来我们来看一下数拆分。
提问:将一个正整数
N
拆成不少于一个数的和,问有多少种方案。
很容易我们可以构造一个多项式:
=Px(0)x0+Px(1)x1+Px(2)x2+...+Px(n)xn
可以发现N的数拆分的方案数正对应着多项式展开后 xn 的系数 Px(n)
考虑如下等式:
因此我们有:
其中上式等式左边是欧拉函数 ϕ(x) 的倒数。即:
欧拉函数
ϕ(x)
的展开式为:
其中的x的指数正对应着广义五边形数!
n | 0 | 1 | -1 | 2 | -2 | 3 | -3 | 4 | -4 | … |
---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 2 | 5 | 7 | 12 | 15 | 22 | 26 | … |
现在我们要计算 Px(n) ,由于 1ϕ(x)=P(x) ,亦即 ϕ(x)P(x)=1 。
所以: Px(n)=Px(n−1)+Px(n−2)−Px(n−5)−Px(n−7)+...
由于对于满足
i(3i−1)2≤n
的
i
的个数不超过
上面我们说明的是不带限制的数拆分,现在我们给定一个限制:拆分出来的每种数的个数不能大于等于k(这也是本题的要求)。
类似的,我们考虑生成函数:
展开 ϕ(xk) 得:
然后可得:
令 Fk(n) 表示n的满足数拆分时每种数的个数小于等于k的数拆分方案数。则有:
my code:
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <bitset>
#include <map>
#include <set>
#include <algorithm>
using namespace std ;
typedef long long LL ;
const int MAXN = 100005 ;
const int mod = 1e9 + 7 ;
int p[MAXN] ;
int t1[MAXN] , t2[MAXN] ;
int n , k ;
void add ( int& x , int y ) {
x += y ;
while ( x >= mod ) x -= mod ;
while ( x < 0 ) x += mod ;
}
void preprocess () {
p[0] = 1 ;
for ( int i = 1 ; ; ++ i ) {
t1[i] = i * ( 3 * i - 1 ) / 2 ;
t2[i] = i * ( 3 * i + 1 ) / 2 ;
if ( t1[i] >= MAXN ) break ;
}
for ( int i = 1 ; i < MAXN ; ++ i ) {
p[i] = 0 ;
for ( int j = 1 , flag = 1 ; ; ++ j , flag *= -1 ) {
if ( t1[j] <= i ) add ( p[i] , flag * p[i - t1[j]] ) ;
else break ;
if ( t2[j] <= i ) add ( p[i] , flag * p[i - t2[j]] ) ;
else break ;
}
}
}
void solve () {
scanf ( "%d%d" , &n , &k ) ;
int ans = p[n] ;
for ( int j = 1 , flag = -1 ; ; ++ j , flag *= -1 ) {
if ( k * t1[j] <= n ) add ( ans , flag * p[n - k * t1[j]] ) ;
else break ;
if ( k * t2[j] <= n ) add ( ans , flag * p[n - k * t2[j]] ) ;
else break ;
}
printf ( "%d\n" , ans ) ;
}
int main () {
int T ;
preprocess () ;
scanf ( "%d" , &T ) ;
for ( int i = 1 ; i <= T ; ++ i ) {
solve () ;
}
return 0 ;
}