五边形数学习小记

学五边形数就是为了整数划分一类问题,目前并不知道有什么其它用途。

设整数划分的生成函数为 P(x) P ( x )

P(x)=i=1(j=1xij) P ( x ) = ∏ i = 1 ∞ ( ∑ j = 1 ∞ x i j )
=i=111xi = ∏ i = 1 ∞ 1 1 − x i

有一不是数论上那个phi的函数 ϕ(x) ϕ ( x ) ,函数式为:
ϕ(x)=i=1(1xi) ϕ ( x ) = ∑ i = 1 ∞ ( 1 − x i )

显然有 ϕ(x)p(x)=1 ϕ ( x ) p ( x ) = 1

那么我们只需要求出 ϕ(x) ϕ ( x ) ,通过多项式求逆的算法即可求出 P(x) P ( x )

我们用一个五边形数定理就能搞定 ϕ(x) ϕ ( x )
ϕ(x)=1+i=1(1)ixi(3i±1)/2 ϕ ( x ) = 1 + ∑ i = 1 ∞ ( − 1 ) i x i ∗ ( 3 i ± 1 ) / 2

证明推荐一篇博客:
《五边形数定理的一种证明》

注意到i在指数中是二次的,也就是说 [x1n]ϕ(x) [ x 1 − n ] ϕ ( x ) 只有大约 n n 个不为0。

这样的话暴力求逆回去就是 O(nn) O ( n n ) 的,与普通dp的复杂度一样,优势在于可以多组询问。

当然可以用NTT多项式求逆,复杂度 O(n log2n) O ( n   l o g 2 n )

裸题是hdu 4651。

没那么裸的是hdu 4658。

第二题的生成函数是:
P(x)=i=1(k=1j=1xij) P ( x ) = ∏ i = 1 ∞ ( ∑ j = 1 k = 1 x i j )
=i=11xki1xi = ∏ i = 1 ∞ 1 − x k i 1 − x i
=ϕ(xk)ϕ(x) = ϕ ( x k ) ϕ ( x )

然后一次询问是 O(n) O ( n )

Code(T1):

#include
#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
#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);
    }
}

你可能感兴趣的:(模版,数论杂集)