题目链接
题目概述
求一个整数(N)的可重无序拆分.
解题思路
稍加变换,原问题等价于方程:
解的个数,可以看做是将(n)个相同的小球放到(n)个相同的盒子允许空盒的数目.对应的结果就是函数:
展开式中(x^n)项的系数.对于(1,x^i,x^{2i},dots)可以理解为使用整数(i,0,1,2,dots)次,得到整数(1,i,2i,dots.)所以展开式中的(x^n)就是(x^{1e_1},x^{2e_2},dots)相乘,使得(x)的指数部分之和是(n.)
注意
因为这里每一个整数(i)使用的数量都没有限制,所以写的是(1,x^i,x^{2i},dots),假设整数(i)使用的次数只能是奇数,那么整数(i)的展开应该是((x^i+x^{3i}+dots));或者整数(i)的使用次数只能是序列({b_1,b_2,dots})中的,那么(i)的展开应该是((x^{ib_1}+x^{ib_2}+dots).)
另外注意上面方程前面那个(e_i)前面的的系数(i),(e_i)表示的是使用这个系数多少次,而这个系数表示另外一种约束,可以这样理解,就是使用一个东西必须要使用(i)次,如果上面的方程变为:
那么对应的母函数就是:
代码实现
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 200;
ll buf[N];
ll temp[N];
void calculate(){
fill(buf, buf + N, 1);
fill(temp, temp + N, 0);
for (int k = 2; k < N; ++k){
for (int i = 0; i < N; ++i){
for(int j = 0; i+j < N; j +=k ){
// temp是当前的k的展开式与上一轮计算的结果.
temp[i + j] += buf[i];
}
}
// 更新这一轮的结果.
memcpy(buf, temp, sizeof(ll)*N);
fill(temp, temp + N, 0);
}
}
int main(int argc, const char** argv) {
calculate();
int n;
while (~scanf("%d",&n)){
printf("%lld
", buf[n]);
}
return 0;
}
下面通过这个表格给出计算((1+x^1+x^2+x^3+x^4)(1+x^2+x^4))的计算过程:
(表格A(上一轮计算的结果))
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
(表格B(这一轮计算的结果))
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
1 | 1 | 1 | 1 | 1 | |||||
1 | 1 | 1 | 1 | 1 | |||||
1 | 1 | 1 | 1 | ||||||
1 | 1 | 1 | 1 | ||||||
1 | 1 | 1 | |||||||
1 | 1 | 1 | |||||||
1 | 1 | ||||||||
1 | 1 | ||||||||
1 | |||||||||
1 |
(整理之后是这一轮计算的值(对应程序中的temp))
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
1 | 1 | 2 | 2 | 3 | 3 | 4 | 4 | 5 | 5 |
(更新这一轮计算的结果准备计算下一轮(对应程序中的buf))
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
1 | 1 | 2 | 2 | 3 | 3 | 4 | 4 | 5 | 5 |
按照上面表格所示的那样,先计算相邻的两项系数作为当前一轮的计算结果,然后在下一轮中座椅一项与下一项进行计算得到下一轮就散的记过,然后更新,这样滚动更新计算,最后的buf
中的系数就是整数拆分的结果(这里为了便于识别,没有将表格中空白的0
显示的标出).