4 4 2 4 3 4 4 4 5
2 4 4 5
题目:http://acm.hdu.edu.cn/showproblem.php?pid=4658
用了五边形数定理以及生成函数,然而我看懂了生成函数怎么搞这题却不知道为啥生成函数是五边形数形式= =
首先观察下面的图片:
很容易我们可以发现用这种方式构造N个五边形(假设一个点也算一个五边形),需要点的个数为:
接下来我们来看一下数拆分。
提问:将一个正整数 N 拆成不少于一个数的和,问有多少种方案。
很容易我们可以构造一个多项式:
P(x)=(1+x1+x2+...)(1+x2+x4+...)(1+x3+x6+...)...
=Px(0)x0+Px(1)x1+Px(2)x2+...+Px(n)xn
可以发现N的数拆分的方案数正对应着多项式展开后 xn 的系数 Px(n)
考虑如下等式:
欧拉函数 ϕ(x) 的展开式为:
n | 0 | 1 | -1 | 2 | -2 | 3 | -3 | 4 | -4 | … |
---|---|---|---|---|---|---|---|---|---|---|
P(n) | 0 | 1 | 2 | 5 | 7 | 12 | 15 | 22 | 26 | … |
现在我们要计算 Px(n) ,由于 1ϕ(x)=P(x) ,亦即 ϕ(x)P(x)=1 。
由于对于满足 i(3i−1)2≤n 的 i 的个数不超过 (√n) 个,于是计算所有 Px(n) 的复杂度为 O(n(√n))
上面我们说明的是不带限制的数拆分,现在我们给定一个限制:拆分出来的每种数的个数不能大于等于k(这也是本题的要求)。
类似的,我们考虑生成函数:
#include<iostream> #include<cstdio> #define NN 100005 #define LL __int64 #define mod 1000000007 using namespace std; LL wu[NN],pa[NN]; void init() { pa[0]=1; pa[1]=1; pa[2]=2; pa[3]=3; LL ca=0; for(LL i=1; i<=100000/2; i++) { wu[ca++]=i*(3*i-1)/2; wu[ca++]=i*(3*i+1)/2; if(wu[ca-1]>100000) break; } for(LL i=4; i<=100000; i++) { pa[i]=(pa[i-1]+pa[i-2])%mod; ca=1; while(wu[2*ca]<=i) { if(ca&1) { pa[i]=(pa[i]-pa[i-wu[2*ca]]); pa[i]=(pa[i]%mod+mod)%mod; if(wu[2*ca+1]<=i) pa[i]=(pa[i]-pa[i-wu[2*ca+1]]),pa[i]=(pa[i]%mod+mod)%mod; } else { pa[i]=(pa[i]+pa[i-wu[2*ca]]); pa[i]=(pa[i]%mod+mod)%mod; if(wu[2*ca+1]<=i) pa[i]=(pa[i]+pa[i-wu[2*ca+1]]),pa[i]=(pa[i]%mod+mod)%mod; } ca++; } } } LL work(int n,int k) { LL ans=pa[n]; LL ca=0; while(k*wu[2*ca]<=n) { if(ca&1) { ans=(ans+pa[n-k*wu[2*ca]]); ans=(ans%mod+mod)%mod; if(k*wu[2*ca+1]<=n) ans=(ans+pa[n-k*wu[2*ca+1]]),ans=(ans%mod+mod)%mod; } else { ans=(ans-pa[n-k*wu[2*ca]]); ans=(ans%mod+mod)%mod; if(k*wu[2*ca+1]<=n) ans=(ans-pa[n-k*wu[2*ca+1]]),ans=(ans%mod+mod)%mod; } ca++; } return ans; } int main() { int T,n,k; init(); scanf("%d",&T); while(T--) { scanf("%d%d",&n,&k); printf("%I64d\n",work(n,k)); } return 0; }