学五边形数就是为了整数划分一类问题,目前并不知道有什么其它用途。
设整数划分的生成函数为
有一不是数论上那个phi的函数
,函数式为:
显然有
那么我们只需要求出 ,通过多项式求逆的算法即可求出
我们用一个五边形数定理就能搞定
:
证明推荐一篇博客:
《五边形数定理的一种证明》
注意到i在指数中是二次的,也就是说 只有大约 个不为0。
这样的话暴力求逆回去就是 的,与普通dp的复杂度一样,优势在于可以多组询问。
当然可以用NTT多项式求逆,复杂度 。
裸题是hdu 4651。
没那么裸的是hdu 4658。
第二题的生成函数是:
然后一次询问是 。
Code(T1):
#include<cstdio>
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define fu(a) ((a) & 1 ? -1 : 1)
using namespace std;
const int mo = 1e9 + 7;
int T, n; ll phi[100005];
int main() {
n = 100000; phi[0] = 1;
fo(i, 1, n) {
fo(j, 1, i) {
int k = j * (3 * j - 1) / 2;
if(k <= i) phi[i] = (phi[i] - fu(j) * phi[i - k] + mo) % mo; else break;
k = j * (3 * j + 1) / 2;
if(k <= i) phi[i] = (phi[i] - fu(j) * phi[i - k] + mo) % mo; else break;
}
}
for(scanf("%d", &T); T; T --)
scanf("%d", &n), printf("%lld\n", phi[n]);
}
Code(T2):
#include<cstdio>
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define fu(a) ((a) & 1 ? -1 : 1)
using namespace std;
const int mo = 1e9 + 7;
int T, n, k; ll p[100005], s;
int main() {
n = 100000; p[0] = 1;
fo(i, 1, n) {
fo(j, 1, i) {
int k = j * (3 * j - 1) / 2;
if(k <= i) p[i] = (p[i] - fu(j) * p[i - k] + mo) % mo; else break;
k = j * (3 * j + 1) / 2;
if(k <= i) p[i] = (p[i] - fu(j) * p[i - k] + mo) % mo; else break;
}
}
for(scanf("%d", &T); T; T --) {
scanf("%d %d", &n, &k);
s = p[n];
fo(i, 1, n) {
int j = i * (3 * i - 1) / 2;
if(j * k <= n) s = (s + fu(i) * p[n - j * k] + mo) % mo; else break;
j = i * (3 * i + 1) / 2;
if(j * k <= n) s = (s + fu(i) * p[n - j * k] + mo) % mo; else break;
}
printf("%lld\n", s);
}
}