知识点 - 分拆数/整数拆分

知识点 - 分拆数/整数拆分

解决问题类型:

将一个数用一个或多个正整数的无序和来表示有几种方案。

这是一个母函数的应用,利用了母函数的指数系数是五边形数来优化复杂度。

结论

(1)称正整数n分解为r个正整数和的个数为n分解成r的分拆数,记为 P r ( n ) P_{r}\left( n \right) Pr(n)

(2) P 1 ( n ) P_{1}\left( n \right) P1(n)=1 P n ( n ) P_{n}\left( n \right) Pn(n)=1 P n − 1 ( n ) P_{n - 1}\left( n \right) Pn1(n)=1 P n − 2 ( n ) P_{n - 2}\left( n \right) Pn2(n)=2 P n − 3 ( n ) P_{n - 3}\left( n \right) Pn3(n)=3

(3) P 2 ( n ) = ⌈ n − 1 2 ⌉ , n ≥ 2 P_{2}\left( n \right) = \left\lceil \frac{n - 1}{2} \right\rceil,n \geq 2 P2(n)=2n1,n2

(4) P r ( n ) = P 1 ( n − r ) + P 2 ( n − r ) + ⋅ ⋅ ⋅ + P r ( n − r ) P_{r}\left( n \right) = P_{1}\left( n - r \right) + P_{2}\left( n - r \right) + \cdot \cdot \cdot + P_{r}\left( n - r \right) Pr(n)=P1(nr)+P2(nr)++Pr(nr)

性质

  1. ·n的分拆数中最大部分为m的个数=把n分拆成m部分的个数
  2. ·n的分拆数中每一部分都小于等于m的个数=把n分成m份或更小
  3. ·n的分拆数中每部分的数都相等的个数=n 的因子个数
  4. ·n的分拆数中每部分都是1或2(或者把n分拆成1或2部分)的个数 = f l o o r ( n / 2 + 1 ) =floor(n/2+1) =floor(n/2+1);
  5. ·n的分拆数中每部分都是1或2或3(或者把n分拆成1或2或3部分)的个数 = ( n + 3 ) 2 / 12 =(n+3)^2/12 =(n+3)2/12;

维基百科翻译

复杂度:

O ( n n ) O(n\sqrt{n}) O(nn )?

例题

将一个正整数N拆成不少于一个数的和,问有多少种方案 :

若令划分函数P(n)为答案,则有
P ( n ) = Σ ( − 1 ) ( i − 1 ) P ( n − q i ) ( q i < = n ) P(n) = \Sigma(-1)^{(i-1)}P(n-q_i) (q_i <= n) P(n)=Σ(1)(i1)P(nqi)(qi<=n)
​ 其中q为五边形数:
q i = 3 i ( i − 1 ) / 2 q_i=3i(i-1)/2 qi=3i(i1)/2
证明(母函数):

将一个正整数N拆成不少于一个数的和,且每个数的出现次数不超过K次

exd:

BZOJ4772 显而易见的数论
Σ 划分方案 p Σ i Σ j > i g ( a F ( p i , p j ) m o d k ) \Sigma _{划分方案p}\Sigma_i\Sigma_{j>i}g(a_F(p_i,p_j)modk) Σ划分方案pΣiΣj>ig(aF(pi,pj)modk)

代码

//T1, https://www.cnblogs.com/geloutingyu/p/7599415.html
#include <iostream>
using namespace std;

const int mod = 1e9 + 7;
const int MAXN = 1e5 + 1;
int f[MAXN];

void get_f(void){
    f[0] = 1;
    for(int i = 1; i < MAXN; i++){
        for(int j = 1, cnt = 1; i - (3 * j * j - j) / 2 >= 0; j++, cnt *= -1){
            int cc = 3 * j * j;
            f[i] += f[i - (cc - j) / 2] * cnt;
            f[i] %= mod;
            f[i] = (f[i] + mod) % mod;
            if(i >= (cc + j) / 2){
                f[i] += f[i - (cc + j) / 2] * cnt;
                f[i] %= mod;
                f[i] = (f[i] + mod) % mod;
            }
        }
    }
}

int main(void){
    get_f();
    int t, x;
    cin >> t;
    while(t--){
        cin >> x;
        cout << f[x] << endl;
    }
    return 0;
}


//T2 T1基础上加个限制 k  
// https://www.cnblogs.com/geloutingyu/p/7603378.html
#include <iostream>
#include <stdio.h>
using namespace std;

const int mod = 1e9 + 7;
const int MAXN = 1e5 + 1;
int f[MAXN];

void get_f(void){
    f[0] = 1;
    for(int i = 1; i < MAXN; i++){
        for(int j = 1, cnt = 1; i - (3 * j * j - j) / 2 >= 0; j++, cnt *= -1){
            int cc = 3 * j * j;
            f[i] += f[i - (cc - j) / 2] * cnt;
            f[i] %= mod;
            f[i] = (f[i] + mod) % mod;
            if(i >= (cc + j) / 2){
                f[i] += f[i - (cc + j) / 2] * cnt;
                f[i] %= mod;
                f[i] = (f[i] + mod) % mod;
            }
        }
    }
}

int solve(int n, int k){
    int ans = f[n];
    for(int i = 1, cnt = -1; n - k * (3 * i * i - i) / 2 >= 0; i++, cnt *= -1){
        ans += f[n - k * (3 * i * i - i) / 2] * cnt;
        ans %= mod;
        ans = (ans + mod) % mod;
        if(n - k * (3 * i * i + i) / 2 >= 0){
            ans += f[n - k * (3 * i * i + i) / 2] * cnt;
            ans %= mod;
            ans = (ans + mod) % mod;
        }
    }
    return ans;
}

int main(void){
    get_f();
    int t, x, k;
    scanf("%d", &t);
    while(t--){
        scanf("%d%d", &x, &k);
        printf("%d\n", solve(x, k));
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Best KeyBoard

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值