您的位置:首页 > 其它

hdu 4651&hdu 4658

2015-07-20 10:58 232 查看

整数拆分

题目描述:

n~1e5

(1) 把n拆分成小于等于n的数的和,问有多少种拆分方法

(2) 把n拆分成小于等于n的数的和, 并且相同数字的使用个数小于k个.

题解:

首先是这道题目的解决方法:

比如1+1+1+2+3+3. 其实可以写成3个1,1个2,2个3,用母函数写就是

(1+x+x^2…)(1+x^2+x^4…)(1+x^3+x^6….)…..代表每个数用了几个最高项小于等于n/i.之后用等比公式(1/(1-x^k)) k从1到无穷.

然后分母是一个欧拉函数,记为Q(x),那么Q(x)可以o(1)的得到次数和系数.

0 1 -1 2 -2 3 -3 4 -4 (下标)

0 1 2 5 7 12 15 22 26(次数)

次数=(3*i*i-i)/2

系数=(-1)^(i)

有了这个,之后是求多项式的倒数.P(x)和Q(x)相乘是1.因此:

P(n)=P(n-1)+P(n-2)-P(n-5)-P(n-7)…..

P(0) = 1;

这样就可以几乎是sqrt(n)n的dp得到.

其次是(2)的解决方法.

修改母函数,本来是加到正无穷,但是有了k的限制,因此每一个加到(x^i)^k-1 就停止了,因此等比公式的分子上面有一个(1-x^i^k),其实就是Q(x^k)/Q(x).也就是上一道题目的P(x)*Q(x^k).根据给出的k,我们仍然o(1

)的能够算出Q(x^k)的系数.合成一下新的n的系数就行了.

重点:

(1)这两道题目的解法:首先变成几个1,几个2的那种,然后写出母函数.根据要求写母函数.之后化成根欧拉函数Q(x)有关的,利用Q(x)的通项公式来算.

(2)整数拆分的性质:

第一.如果每个数不重复,那么用dp可以算.

第二. 如果可以开两维,那么dp中含有个数也可以算. (n,num)=(n-num,num)+(n,num-1)

第三.用<=k的数来拆分等价于用<=k个数来拆分.进一步,等价于n+k拆分成恰好k个数的个数.

第四.将n表达成奇数之和的方法等于两两个数不同.

代码:

const int MOD = 1e9+7;
int dp[100010];
void init()//构造出P(x)
{
memset(dp,0,sizeof(dp));
dp[0] = 1;
for(int i = 1;i <= 100000;i++)
{
for(int j = 1, r = 1; i - (3 * j * j - j) / 2 >= 0; j++, r *= -1)
{
dp[i] += dp[i -(3 * j * j - j) / 2] * r;
dp[i] %= MOD;
dp[i] = (dp[i]+MOD)%MOD;
if( i - (3 * j * j + j) / 2 >= 0 )
{
dp[i] += dp[i - (3 * j * j + j) / 2] * r;
dp[i] %= MOD;
dp[i] = (dp[i]+MOD)%MOD;
}
}
}
}
int solve(int n,int k)//构造出Q(x^k)
{
int ans = dp
;
for(int j = 1, r = -1; n - k*(3 * j * j - j) / 2 >= 0; j++, r *= -1)
{
ans += dp[n -k*(3 * j * j - j) / 2] * r;
ans %= MOD;
ans = (ans+MOD)%MOD;
if( n - k*(3 * j * j + j) / 2 >= 0 )
{
ans += dp[n - k*(3 * j * j + j) / 2] * r;
ans %= MOD;
ans = (ans+MOD)%MOD;
}
}
return ans;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: