hdoj 4767 Bell 【矩阵快速幂 + CRT】



Bell

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 666    Accepted Submission(s): 286


Problem Description
What? MMM is learning Combinatorics!?
Looks like she is playing with the bell sequence now:
bell[n] = number of ways to partition of the set {1, 2, 3, ..., n}

e.g. n = 3:
{1, 2, 3}
{1} {2 3}
{1, 2} {3}
{1, 3} {2}
{1} {2} {3}
so, bell[3] = 5

MMM wonders how to compute the bell sequence efficiently.
 

Input
T -- number of cases
for each case:
  n (1 <= n < 2^31)
 

Output
for each case:
  bell[n] % 95041567
 

Sample Input
      
      
6 1 2 3 4 5 6
 

Sample Output
      
      
1 2 5 15 52 203
 



Bell数:讲解特详细



看会上面这个,就可以KO这道题了。


先找到95041567的小于100的质因子:

#include <cstdio>
int main()
{
    int n = 95041567;
    while(n != 1)
    {
        for(int i = 2; i <= 100; i++)//没有必要判断是否是质数 就可以找到。。。
        {
            if(n % i == 0)
            {
                printf("%d\n", i);
                n /= i;
                break;
            }
        }
    }
    return 0;
}




我构建的矩阵如下:



AC代码:


#include <cstdio>
#include <cstring>
#include <algorithm>
#define LL long long
#define debug printf("1\n");
using namespace std;
int m[6] = {0, 31, 37, 41, 43, 47};
int rm[6];
int a[6];//方程组x % m = a
int B[6][60][60];//保存对m[i]取余后的 Bell数
int F[60];
void getBell(int id, int MOD)//预处理贝尔数
{
    B[id][0][1] = 1;
    for(int i = 1; i <= 50; i++)
    {
        for(int j = 1; j <= i+1; j++)
        {
            if(j == 1)
                B[id][i][j] = B[id][i-1][i];
            else
                B[id][i][j] = (B[id][i-1][j-1] + B[id][i][j-1]) % MOD;
        }
    }
}
struct MATRIX
{
    struct Matrix{
        int a[60][60];
        int r, c;
    };
    Matrix ori, res;
    void init(int id, int p)//建立p*p矩阵
    {
        for(int i = 1; i <= p; i++)
            F[i] = B[id][i][1] % p;
        //debug;
        ori.r = res.r = ori.c = res.c = p;
        memset(ori.a, 0, sizeof(ori.a));
        memset(res.a, 0, sizeof(res.a));
        for(int i = 1; i <= p; i++)
        {
            res.a[i][i] = 1;
            if(i < p)
                ori.a[i][i] = ori.a[i+1][i] = 1;
            else
                ori.a[i][i] = ori.a[1][i] = ori.a[2][i] = 1;
        }
    }
    Matrix multi(Matrix x, Matrix y, int MOD)
    {
        Matrix z;
        memset(z.a, 0, sizeof(z.a));
        z.r = x.r; z.c = y.c;
        for(int i = 1; i <= x.r; i++)
        {
            for(int k = 1; k <= x.c; k++)
            {
                if(x.a[i][k] == 0) continue;
                for(int j = 1; j <= y.c; j++)
                    z.a[i][j] = (z.a[i][j] + (x.a[i][k] * y.a[k][j]) % MOD) % MOD;
            }
        }
        return z;
    }
    void solve(int n, int MOD)
    {
        while(n)
        {
            if(n & 1)
                res = multi(ori, res, MOD);
            ori = multi(ori, ori, MOD);
            n >>= 1;
        }
    }
};
MATRIX MA;
struct CRT
{
    int gcd(int a, int b){
        return b == 0 ? a : gcd(b, a%b);
    }
    void exgcd(int a, int b, int &d, int &x, int &y)
    {
        if(b == 0) {d = a, x = 1, y = 0;}
        else
        {
            exgcd(b, a%b, d, y, x);
            y -= x * (a / b);
        }
    }
    int solve(int l, int r, int *m, int *a)
    {
        int LCM = 1;
        for(int i = l; i <= r; i++)
            LCM = LCM / gcd(LCM, m[i]) * m[i];
        for(int i = l+1; i <= r; i++)
        {
            int A = m[l], B = m[i], d, x, y, c = a[i] - a[l];
            exgcd(A, B, d, x, y);
            if(c % d) return -1;
            int mod = m[i] / d;
            int k = ((x * c / d) % mod + mod) % mod;
            a[l] = m[l] * k + a[l];
            m[l] = m[l] / d * m[i];
        }
        if(a[l] == 0) return LCM;
        return a[l];
    }
};
CRT crt;
int main()
{
    for(int i = 1; i <= 5; i++)
        getBell(i, m[i]);
    int t;
    int n;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d", &n);
        if(n <= 50)
        {
            for(int i = 1; i <= 5; i++)
                a[i] = B[i][n][1];
            memcpy(rm, m, sizeof(m));
            printf("%d\n", crt.solve(1, 5, rm, a));
            continue;
        }
        for(int i = 1; i <= 5; i++)
        {
            MA.init(i, m[i]);//构建矩阵
            int temp = n / m[i];
            if(n % m[i] == 0)
                temp -= 1;
            MA.solve(temp, m[i]);
            int remain = n % m[i];//找到位置 即第多少列
            if(remain == 0)
                remain = m[i];
            a[i] = 0;
            for(int j = 1; j <= m[i]; j++)//直接求解当前列的值就可以了
                a[i] = (a[i] + (MA.res.a[j][remain] * F[j]) % m[i]) % m[i];
        }
        memcpy(rm, m, sizeof(m));//一开始忘了这个了。。。
        printf("%d\n", crt.solve(1, 5, rm, a));//求解
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值