sgu441. Set Division 矩阵快速幂 第二类斯特林数

http://acm.sgu.ru/problem.php?contest=0&problem=441

441. Set Division
Time limit per test: 0.25 second(s)
Memory limit: 262144 kilobytes
input: standard
output: standard



Ruslan has  K  friends. And all of them have birthday tomorrow. He has bought  N  different photo albums yesterday, and wants to present these photo albums to his friends. Of course, he can't give less than 1 photo album to anybody. Your task is to calculate how many possible ways is there to do it.

All photo albums are different. Two distributions are considered the same if they differ only by order of albums in the gifts or by the persons receiving gifts.

Input
In the only line of the input there are two numbers separated by a space —  .

Output
Output should contain one number — the number of possible ways to distribute the albums modulo 2007.

Example(s)
sample input
sample output
3 2 
3

题意:将n件不同礼物分给k个人,每个人至少一件,问有多少种分发。

思路:f[i][j]表示把前i件礼物分给j个人的种类数 f[i][j]=f[i-1][j]*j+f[i-1][j-1]

但是此题的n特别大,而k最大只有10,一般的dp肯定会超时/空间,但是可以用矩阵加速。

这里所有的f[i]为一个状态,行向量为[ f[1][0], f[1][1], f[1][2]...f[1][k] ] ,构造矩阵k+1行k+1列

1 1 0 0 0……0

0 1 1 0 0……0

0 0 2 1 0……0

0 0 0 3 1……0

最后的结果就是第二行第k列。

/**
 * @author neko01
 */
//#pragma comment(linker, "/STACK:102400000,102400000")
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define min3(a,b,c) min(a,min(b,c))
#define max3(a,b,c) max(a,max(b,c))
#define pb push_back
#define mp(a,b) make_pair(a,b)
#define clr(a) memset(a,0,sizeof a)
#define clr1(a) memset(a,-1,sizeof a)
#define dbg(a) printf("%d\n",a)
typedef pair<int,int> pp;
const double eps=1e-9;
const double pi=acos(-1.0);
const int INF=0x3f3f3f3f;
const LL inf=(((LL)1)<<61)+5;
const int N=11;
const int mod=2007;
struct Matrix{
    int a[N][N];
    int sz;
    Matrix(int x,int n){
        sz=n;
        for(int i=0;i<N;i++)
            for(int j=0;j<N;j++)
                a[i][j]=(i==j)?x:0;
    }
    Matrix operator*(Matrix &b){
        Matrix tmp(0,sz);
        for(int i=0;i<sz;i++)
            for(int j=0;j<sz;j++)
                for(int k=0;k<sz;k++)
                    tmp.a[i][j]+=a[i][k]*b.a[k][j],tmp.a[i][j]%=mod;
        return tmp;
    }
    Matrix operator^(int k){
        Matrix cur=*this,res(1,sz);
        while(k){
            if(k&1) res=res*cur;
            cur=cur*cur;
            k>>=1;
        }
        return res;
    }
};
//f[i][j]=f[i-1][j]*j+f[i-1][j-1]
int main()
{
    int n,k;
    scanf("%d%d",&n,&k);
    Matrix t=Matrix(0,k+1);
    t.a[0][0]=1;
    for(int i=1;i<=k;i++)
        t.a[i-1][i]=1,t.a[i][i]=i;
    t=t^(n-1);
    printf("%d\n",t.a[1][k]);
    return 0;
}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值