记一些数学引理和结论等

@

一些结论

\(gcd(x^a-1,x^b-1)=x^{gcd(a,b)}-1\)
\(gcd(fib[x],fib[y])=fib[gcd(x,y)]\)


费马大定理

定理:\(a^n+b^n=c^n\;\;(n\ge3时没有整数解)\)
扩展:当\(a=2*k+1\)为奇数时,\(c=k^2+(k+1)^2,b=c-1\);当\(a=2*k\)为偶数时,\(c=k^2+1,b=c-2\)


第二类斯特林数

\(S(n,k)\)一个\(n\)元集的所有\(k\)划分的个数。\(n\)个不同的球放到\(k\)个同样的盒子使无空盒。
定理1:\(S(n+1,k)=S(n,k-1)+k\times S(n,k)\)
定理2:\(S(n,2)=2^{n-1}-1,S(n,n-1)=C_2^n\)
定理3:\(S(n+1,k)=C_{k-1}^nS(k-1,k-1)+C_{k}^nS(k,k-1)+...+C_{n}^nS(n,k-1)\)


整数的无序拆分

\(B(n,k)=0,\;n<k\)
\(B(n,1)=B(n,n)=1\)
\(H(n)=B(n,1)+B(n,2)+...+B(n,n)\)
定理1:\(B(n+k,k)=B(n,1)+B(n,2)+...+B(n,k)\)

  • 解法1:记忆化搜索(复杂度接近\(O(n^2)\))

\(dfs(n, m)\) 表示用\(1-m\)的数字无序分解\(n\)的方案数。

1.当\(n==m\)时:你可以选择用\(n\)分解得到\(1\)个方案,加上不用\(n\)分解的方案\(dfs(n,m-1)\)
2.当\(n>m\)时:你可以选择用\(m\)分解得到方案\(dfs(n-m,m)\),加上不用\(m\)分解的方案\(dfs(n,m-1)\)
3.当\(n<m\)时:就等于\(dfs(n,n)\)

当然把记忆化搜索改成递推也很好写,常数可能小点???

const int mod = 1e9 + 7;
const int MXN = 1e4 + 7;
int n;
LL dp[MXN][MXN];
LL dfs(int n, int m) {//用1-m的数字无序分解n的方案数
    if(n == 1 || m == 1) return 1;
    if(dp[n][m] != -1) return dp[n][m];
    LL ans = 0;
    if(n == m) ans = dfs(n, m-1) + 1;
    if(n < m) ans = dfs(n, n);
    if(n > m) ans = dfs(n, m-1) + dfs(n-m, m);
    ans %= mod;
    dp[n][m] = ans;
    return ans;
}
int main(int argc, char const *argv[]){
    scanf("%d", &n);
    memset(dp, -1, sizeof(dp));
    printf("%lld\n", dfs(n, n));
    memset(dp, -1, sizeof(dp));
    for(int i = 1; i <= n; ++i) dp[i][1] = 1, dp[1][i] = 1;
    for(int i = 2; i <= n; ++i) {
        for(int j = 2; j <= n; ++j) {
            if(i == j) dp[i][j] = (dp[i][j-1] + 1)%mod;
            else if(i > j) dp[i][j] = (dp[i][j-1] + dp[i-j][j])%mod;
            else dp[i][j] = dp[i][i];
        }
    }
    printf("%lld\n", dp[n][n]);
    return 0;
}
  • 解法2:母函数法之暴力解多项式乘法

\(G(x)=(x^0+x^1+...+x^n)\times (x^0+x^2+x^4+...+x^{2\times \frac n2})\times ...\times(x^0+x^{n\times \frac nn})\)
用两个数组迭代暴力求解。
母函数法应该还可以优化吧?

const int mod = 1e9 + 7;
const int MXN = 1e5 + 7;
int n;
LL n1[MXN], n2[MXN], v[MXN];
LL a[MXN], b[MXN];
void init(int n) {
    for(int i = 1; i <= n; ++i) {
        n1[i] = 0; n2[i] = n/i; v[i] = i;
        b[i] = 1; a[i] = 0;
    }
    b[0] = 1;
}
void solve(int n) {
    for(int i = 2; i <= n; ++i) {
        memcpy(a, b, sizeof(LL)*(n+2));
        memset(b, 0, sizeof(LL)*(n+2));
        for(int j = n1[i]; j <= n2[i] && j*v[i] <= n; ++j) {
            for(int k = 0; k+j*v[i] <= n; ++k) {
                b[k+j*v[i]] = (b[k+j*v[i]]+a[k])%mod;
            }
        }
    }
    printf("%lld\n", b[n]);
}
int main(int argc, char const *argv[]){
    scanf("%d", &n);
    init(n);
    solve(n);
    return 0;
}

再贴一个母函数的暴力模板:

int n;
LL n1[MXN], n2[MXN], v[MXN];
LL a[MXN], b[MXN];
int last;
void init(int n) {
    for(int i = 1; i <= n; ++i) {
        n1[i] = 0; n2[i] = n/i; v[i] = i;
        b[i] = 0; a[i] = 0;
    }
    b[0] = 1;
    last = 0;
}
void solve(int n) {
    for(int i = 1; i <= n; ++i) {
        int last2 = min((LL)n, last+n2[i]*v[i]);
        memcpy(a, b, sizeof(LL)*(last2+2));
        memset(b, 0, sizeof(LL)*(last2+2));
        for(int j = n1[i]; j <= n2[i] && j*v[i] <= last2; ++j) {
            for(int k = 0; k <= last && k+j*v[i] <= last2; ++k) {
                b[k+j*v[i]] = (b[k+j*v[i]]+a[k])%mod;
            }
        }
        last = last2;
    }
    printf("%lld\n", b[n]);
}
int main(int argc, char const *argv[]){
    scanf("%d", &n);init(n);solve(n);
    return 0;
}

输出无序拆分的解:
在这里插入图片描述

#include <stdio.h>
//使用一个数组记录在递归过程中产生的前面需要重复输出的值
int set[100];
//用于在递归过程中判断是否递归到最深处,输出回车
int k;
//此函数表示使用不大于m的整数对n进行拆分的情况,i用于表示set数组已经存在的记录数长度
void q(int n,int m,int i){
    if(n==k&&n!=m){ 
        //此时递归栈已经退回到某一分支的最上层,输出回车
        //并重置计数器i为0
        printf("\n");
        i=0;
    }
    if(n==1){
        //当n为1,意味者着只能表示1
        printf("1 ");
        return;
    }
    else if(m==1){
        //当m为1,意味着要输出n个m相加
        for(int i=0; i<n-1; i++)
            printf("1+");
        printf("1 ");
        return;
    }
    if(n<m) {
        q(n,n,i);
    }
    if(n==m){
        //当n等于m时,到达本次递归求和的一个叶子,此时需要输出多一个空格,表示下一次输出为另一个叶子
        printf("%d ",n);
        //在递归输出另一个叶子之前,将之前记录的在叶子之上的数一并输出,如上图示过程1
        for(int j=0; j<i; j++)
            printf("%d+",set[j]);
        q(n,m-1,i);
        
    }
    if(n>m){
        //如果n大于m,使用m作为分解,则要首先输出m+的形式
        printf("%d+",m);
        //记录下作为树干节点m的值并使i自增
        set[i++]=m;
        //递归输出m+以后的分解
        q(n-m,m,i);
        //递归完毕后需要将数组记录后退一个,回到上一个节点,如上图示过程2
        i--;
        //执行另一个分支,在下一次递归之前输出记录的数据,如上图示过程3
        for(int j=0; j<i; j++)
            printf("%d+",set[j]);
        //递归输出另一分支情况
        q(n,m-1,i);
    }
}
int main(){
    int n;
    while(scanf("%d",&n)!=EOF){
        if(n<=0){
            printf("Please input a positive interger.\n\n");
            continue;
        }
        k=n;
        q(n,n,0);
        printf("\n\n");
    }
    return 0;
}


伯努利数模板

\(B_0=1,B_1=-\frac 12,B_2=\frac 16,B_3=0,B_4=-\frac 1{30}\)
\(B_5=0,B_6=\frac1{42},B_7=0,B_8=-\frac1{30},B_9=0\)
nlog(n)

//day7 J oeis
//n%4==0和n=1时Bnl[n]是负的
//a(n) = abs[c(2*n-1)] where c(n)= 2^(n+1) * (1-2^(n+1)) * Ber(n+1)/(n+1)
#include<bits/stdc++.h>
#pragma comment(linker, "/STACK:1024000000,1024000000")
namespace BNL{
    using namespace std;
    typedef long long LL;
    const int N = 300020;
    //const LL P = 50000000001507329LL; //190734863287 * 2 ^ 18 + 1 G = 3 常数巨大
    //const int P = 1004535809; //479 * 2 ^ 21 + 1 G = 3
    const int P = 998244353; // 119 * 2 ^ 23 + 1 G = 3
    //const int P = 104857601;  // 25 * 2 ^ 22 + 1 G = 3
    //const int P = 167772161; // 5 * 2 ^ 25 + 1 G = 3
    const int G = 3;
    int wn[25];
    LL mul(LL x, LL y) {return (x * y - (LL)(x / (long double)P * y + 1e-3) * P + P) % P;}
    int qpow(LL a, int b, int p) {
        LL res = 1;
        for(;b;b>>=1,a=a*a%p) {
            if(b&1) res=res*a%p;
        }
        return (int)res;
    }
    void getwn() {
        for(int i = 1; i <= 21; ++i) {
            int t = 1 << i;
            wn[i] = qpow(G, (P - 1) / t, P);
        }
    }
    void change(int *y, int len) {
        for(int i = 1, j = len / 2; i < len - 1; ++i) {
            if(i < j) swap(y[i], y[j]);
            int k = len / 2;
            while(j >= k) {
                j -= k;k /= 2;
            }
            j += k;
        }
    }
    void NTT(int *y, int len, int on) {
        change(y, len);
        int id = 0;
        for(int h = 2; h <= len; h <<= 1) {
            ++id;
            for(int j = 0; j < len; j += h) {
                int w = 1;
                for(int k = j; k < j + h / 2; ++k) {
                    int u = y[k];
                    int t = 1LL * y[k+h/2] * w % P;
                    y[k] = u + t;
                    if(y[k] >= P) y[k] -= P;
                    y[k+h/2] = u - t + P;
                    if(y[k+h/2] >= P) y[k+h/2] -= P;
                    w = 1LL * w * wn[id] % P;
                }
            }
        }
        if(on == -1) {
            for(int i = 1; i < len / 2; ++i) swap(y[i], y[len-i]);
            int inv = qpow(len, P - 2, P);
            for(int i = 0; i < len; ++i)
                y[i] = 1LL * y[i] * inv % P;
        }
    }
    int tmp[N];
    void get_inv(int A[], int A0[], int t) {
        if(t == 1) {
            A0[0] = qpow(A[0], P - 2, P);
            return;
        }
        get_inv(A, A0, t / 2);
        for(int i = 0; i < t; ++i) tmp[i] = A[i];
        for(int i = t; i < 2 * t; ++i) tmp[i] = 0;
        for(int i = t / 2; i < 2 * t; ++i) A0[i] = 0;
        NTT(tmp, 2 * t, 1);
        NTT(A0, 2 * t, 1);
        for(int i = 0; i < 2 * t; ++i) {
            tmp[i] = (2 - 1LL * tmp[i] * A0[i] % P) % P;
            if(tmp[i] < 0) tmp[i] += P;
            A0[i] = 1LL * A0[i] * tmp[i] % P;
        }
        NTT(A0, 2 * t, -1);
    }
    int B[N], f[N], nf[N], a[N];
    void init() {
        f[0] = 1;
        for(int i = 1; i < N; ++i) f[i] = 1LL * f[i-1] * i % P;
        nf[N-1] = qpow(f[N-1], P - 2, P);
        for(int i = N - 2; i >= 0; --i) {
            nf[i] = 1LL * nf[i+1] * (i + 1) % P;
        }
        for(int i = 0; i < N - 1; ++i) a[i] = nf[i+1];
        int len = 1 << 17;
        get_inv(a, B, len);
        for(int i = 0; i < len; ++i) B[i] = 1LL * B[i] * f[i] % P;
    }
    void solve_bnl() {
        getwn();//最前面
        init();
    }
}
int main() {
    BNL::solve_bnl();
    printf("%d\n", BNL::B[2]);
    return 0;
}


#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
namespace POLY{
    const int MAXN = 262144;
    const int MOD = 998244353, ROOT = 3;
    typedef vector<int> Poly;
    inline int add(int a, int b){ return (a + b) % MOD; }
    inline int sub(int a, int b){ return (a - b + MOD) % MOD; }
    inline int mul(int a, int b){ return (long long)a * b % MOD; }
    int power(int a, int b){
        int ret = 1;
        for (int t = a; b; b >>= 1){
            if (b & 1)ret = mul(ret, t);
            t = mul(t, t);
        }
        return ret;
    }
    int pow2(int n){
        int ret = 1;
        while (ret < n)ret *= 2;
        return ret;
    }
    void clean(Poly& p){
        while (!p.empty() && !p.back())p.pop_back();
    }
    Poly cut(const Poly& p, int n){
        return Poly(p.begin(), p.begin() + n);
    }
    Poly expand(const Poly& p, int n){
        Poly ret(n);
        copy(p.begin(), p.end(), ret.begin());
        return ret;
    }
    Poly operator + (Poly p, const Poly& p2){
        p.resize(max(p.size(), p2.size()));
        for (int i = 0; i < (int)p2.size(); i++)
            p[i] = add(p[i], p2[i]);
        return p;
    }
    Poly operator - (Poly p, const Poly& p2){
        p.resize(max(p.size(), p2.size()));
        for (int i = 0; i < (int)p2.size(); i++)
            p[i] = sub(p[i], p2[i]);
        return p;
    }
    Poly operator * (Poly p, int x){
        for (int i = 0; i < (int)p.size(); i++)
            p[i] = mul(p[i], x);
        return p;
    }
    void dot(Poly& p, const Poly& p2){
        for (int i = 0; i < (int)p.size(); i++)
            p[i] = mul(p[i], p2[i]);
    }
    void fft(Poly& a, bool rev){
        static int g[MAXN + 1], w[MAXN] ;
        if (!g[0]){
            w[0] = 1;
            g[0] = 1; g[1] = power(ROOT, (MOD - 1) / MAXN);
            for (int i = 2; i < MAXN; i++)
                g[i] = mul(g[i - 1], g[1]);
        }
        int len = a.size(), d = MAXN / len;
        for (int i = 1, j = len / 2; i < len; i++){
            w[i] = g[d * (rev ? len - i : i)];
            if (i < j) swap(a[i], a[j]);
            for (int k = len; j < k; k >>= 1, j ^= k);
        }
        for (int s = 1; s < len; s <<= 1){
            int step = len / s / 2;
            for (int j = 0; j < len; j += 2 * s){
                for (int k = j, wk = 0; k < j + s; k++, wk += step){
                    int t = mul(w[wk], a[k + s]);
                    a[k + s] = sub(a[k], t); a[k] = add(a[k], t);
                }
            }
        }
        if (rev){
            int t = MOD / len; t = mul(t, t * len);
            for (int i = 0; i < len; i++)
                a[i] = mul(a[i], t);
        }
    }
    Poly operator * (const Poly& p1, const Poly& p2){
        if (p1.empty() || p2.empty())return Poly();
        int len = pow2(p1.size() + p2.size());
        Poly p3 = expand(p1, len), p4 = expand(p2, len);
        fft(p3, 0); fft(p4, 0);
        dot(p3, p4); fft(p3, 1);
        p3.resize(p1.size() + p2.size() - 1);
        return p3;
    }
    Poly inv(const Poly& p){
        int len = 2 * pow2(p.size());
        Poly a, b(len), exa = expand(p, len);
        b[0] = power(p[0], MOD - 2);
        for (int i = 4; i <= len; i *= 2){
            a = cut(exa, i / 2);
            a.resize(i); b.resize(i);
            fft(a, 0); fft(b, 0); dot(a, b);
            dot(b, Poly(i, 2) - a);
            fft(b, 1); b.resize(i / 2);
        }
        b.resize(p.size());
        return b;
    }
    int fact[MAXN], factInv[MAXN];
    void init(){
        fact[0] = 1;
        for (int i = 1; i < MAXN; i++)
            fact[i] = mul(fact[i - 1], i);
        factInv[MAXN - 1] = power(fact[MAXN - 1], MOD - 2);
        for (int i = MAXN - 1; i; i--)
            factInv[i - 1] = mul(factInv[i], i);
    }
    Poly bernoulli(int n){//MAXN是n的至少两倍
        Poly p(n + 1);
        for (int i = 0; i <= n; i++)
            p[i] = factInv[i + 1];
        return inv(p);
    }
    vector<int> Bnl;
    const int maxn = 2e5 + 7;
    int jie[maxn];
    void solve_bnl() {
        jie[1] = jie[0] = 1;
        for(int i = 2; i < maxn; ++i) jie[i] = 1LL*jie[i-1]*i%MOD;
        init();
        Bnl = bernoulli(100010);
        for(int i = 0; i < 100010; ++i) Bnl[i]=Bnl[i]*1LL*jie[i]%MOD;
    }
}
int main() {
    POLY::solve_bnl();
    printf("%d\n", POLY::Bnl[2]);
    return 0;
}
posted @ 2019-03-01 21:08 Cwolf9 阅读(...) 评论(...) 编辑 收藏

Contact with me