UVA 12297 Super Poker

UVA_12297

    在研究了好一阵子标程之后终于把标程的解体思路弄懂了,其实关键之处就在于标程给出的递推式:f(n, k) = f(n-k, k) + f(n-k, k-1) * 4 + f(n-k, k-2) * 6 + f(n-k, k-3) * 4 + f(n-k, k-4)。

    这里f(n, k)表示用k张牌组成和为N的方案数,在递推的时候考虑一共有多少张1。①考虑有0张1:这时就相当于用k张没有任何限制的牌组成和为n-k,然后将每张牌的点数+1,这样自然就没有1了,这部分的方案数是f(n-k, k);②考虑有1张1:这时就相当于用k-1张没有任何限制的牌组成和为n-k,然后将每张牌的点数+1,这样也就没有1了,这时总和是n-1,再从4张1种任选一张1就可以使总和为n,这样就恰好用了k张牌,而且k张牌中只有1个1,这部分的方案数是f(n-k, k-1) * C(4,1),也就是f(n-k, k-1) * 4;③考虑有2张1:……(剩下的情况类似,就不再赘述了)。

    有了这个递推式后就会发现是可以用矩阵加速运算了,由于那个矩阵确实有点大,就不再贴图了,在纸上画一画之后还是不难构造出来的。

    此外还有一种解法,但是始终没看懂什么意思,如果各位仁兄看懂了的话还望教小弟一下,链接:http://hi.baidu.com/wjbzbmr/blog/item/88c8b93aed71fb38b9998f45.html

 

View Code (标程)
#include <set>
#include <map>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <queue>
#include <stack>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <cassert>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long i64;

const int MOD = 1000000009;
const int NN = 256;

// recurrence: f(n, k) = f(n-k, k) + f(n-k, k-1) * 4 + f(n-k, k-2) * 6 + f(n-k, k-3) * 4 + f(n-k, k-4)

int n, k, N, pos[18][18];;
int base[NN][NN], res[NN][NN], temp[NN][NN];

int multiply( int A[NN][NN], int B[NN][NN], int C[NN][NN] ) {
    for( int i = 0; i < N; i++ ) for( int j = 0; j < N; j++ ) {
        temp[i][j] = 0;
        for( int k = 0; k < N; k++ ) if( A[i][k] && B[k][j] ) temp[i][j] = ( temp[i][j] + A[i][k] * (i64)B[k][j] ) % MOD;
    }
    for( int i = 0; i < N; i++ ) for( int j = 0; j < N; j++ ) C[i][j] = temp[i][j];
}

void buildBase() {
    for( int i = 0; i < N; i++ ) {
        for( int j = 0; j < N; j++ ) base[i][j] = res[i][j] = 0;
        res[i][i] = 1;
    }
    // build base
    int x = 0;
    for( int j = k; j >= 1; j-- ) for( int i = k + 1; i > 1; i-- ) {
        if( pos[i][j] == -1 ) {
            int p = i - j, q = j;
            if( p >= 1 ) {
                assert( pos[p][q] > -1 ); base[x][ pos[p][q] ] = 1;
                q--;
                if( q > 0 ) {
                    assert( pos[p][q] > -1 ); base[x][ pos[p][q] ] = 4;
                    q--;
                    if( q > 0 ) {
                        assert( pos[p][q] > -1 ); base[x][ pos[p][q] ] = 6;
                        q--;
                        if( q > 0 ) {
                            assert( pos[p][q] > -1 ); base[x][ pos[p][q] ] = 4;
                            q--;
                            if( q > 0 ) {
                                assert( pos[p][q] > -1 ); base[x][ pos[p][q] ] = 1;
                            }
                        }
                    }
                }
            }
        }
        else base[x][ pos[i][j] ] = 1;
        x++;
    }
}

int solve() {
    int dp[20][17] = {0};

    dp[0][0] = 1;
    for( int i = 1; i < 20; i++ ) {
        for( int j = 1; j <= k; j++ ) {
            int p = i - j, q = j;
            if( p >= 0 && q >= 0 ) dp[i][j] = (dp[i][j] + dp[p][q]) % MOD;
            q--; if( p >= 0 && q >= 0 ) dp[i][j] = (dp[i][j] + 4 * (i64) dp[p][q]) % MOD;
            q--; if( p >= 0 && q >= 0 ) dp[i][j] = (dp[i][j] + 6 * (i64) dp[p][q]) % MOD;
            q--; if( p >= 0 && q >= 0 ) dp[i][j] = (dp[i][j] + 4 * (i64) dp[p][q]) % MOD;
            q--; if( p >= 0 && q >= 0 ) dp[i][j] = (dp[i][j] + dp[p][q]) % MOD;
        }
    }
    if( n < 20 ) {
        int r = 0;
        for( int i = 1; i <= k; i++ ) r = ( r + dp[n][i] ) % MOD;
        return r;
    }
    int f[NN] = {0}, f1[NN] = {0};
    N = 0;
    memset( pos, -1, sizeof(pos) );
    for( int i = k; i >= 1; i-- ) for( int j = k; j >= 1; j-- ) {
        f[N] = dp[j][i];
        pos[j][i] = N++;
    }

    buildBase();

    int p = n - k;

    while( p ) {
        if( p & 1 ) multiply( res, base, res );
        multiply( base, base, base );
        p >>= 1;
    }

    for( int i = 0; i < N; i++ ) for( int k = 0; k < N; k++ ) if( f[k] && res[i][k] ) f1[i] = (f1[i] + res[i][k] * (i64)f[k]) % MOD;

    int r = 0;
    for( int i = 0; i < N; i += k ) r = ( r + f1[i] ) % MOD;
    return r;
}

int main() {
    double cl = clock();

    while( scanf("%d %d", &n, &k) == 2 && n ) {
        printf("%d\n", solve());
    }

    cl = clock() - cl;
    fprintf(stderr, "Total Execution Time = %lf seconds\n", cl / CLOCKS_PER_SEC);

    return 0;
}

 

View Code (My code)
#include<stdio.h>
#include<string.h>
#define MAXD 120
#define MOD 1000000009
typedef long long LL;
int temp[MAXD][MAXD], f[15][15], N, K;
int n;
const int d[] = {1, 4, 6, 4, 1};
struct matrix
{
    int a[MAXD][MAXD];
    void init(int x)
    {
        for(int i = 0; i < n; i ++)
            for(int j = 0; j < n; j ++)
                a[i][j] = x;
    }
    matrix operator * (const matrix &t) const
    {
        matrix ans;
        ans.init(0);
        for(int i = 0; i < n; i ++)
            for(int k = 0; k < n; k ++)
                if(a[i][k])
                {
                    for(int j = 0; j < n; j ++)
                        if(t.a[k][j])
                            ans.a[i][j] = (ans.a[i][j] + (LL)a[i][k] * t.a[k][j]) % MOD;
                }
        
        return ans;
    }
}mat, unit;
void prepare()
{
    memset(f[0], 0, sizeof(f[0]));
    f[0][0] = 1;
    for(int i = 1; i <= 10; i ++)
        for(int j = 1; j <= 10; j ++)
        {
            f[i][j] = 0;
            if(i >= j)
            {
                for(int k = 0; k < 5 && j - k >= 0; k ++)
                    f[i][j] = (f[i][j] + (LL)d[k] * f[i - j][j - k]) % MOD;
            }
        }
}
void build()
{
    n = K * (K + 1);
    mat.init(0);
    for(int i = 0; i < K; i ++)
        for(int j = 0; j <= K; j ++)
            mat.a[i * (K + 1) + j][0] = f[K - 1 - i][j];
    
    unit.init(0);
    for(int j = 1; j <= K; j ++)
    {
        for(int k = 0; k < 5 && j - k >= 0; k ++)
            unit.a[j][(j - 1) * (K + 1) + j - k] = d[k];
    }
    for(int i = 1; i < K; i ++)
        for(int j = 0; j <= K; j ++)
            unit.a[i * (K + 1) + j][(i - 1) * (K + 1) + j] = 1;    
}
void powmod(int n)
{
    while(n)
    {
        if(n & 1)
            mat = unit * mat;
        n >>= 1, unit = unit * unit;    
    }
}
void solve()
{
    if(N <= 10)
    {
        int ans = 0;
        for(int i = 1; i <= K; i ++)
            ans = (ans + f[N][i]) % MOD;
        printf("%d\n", ans);
        return ;
    }
    build();
    powmod(N - K + 1);
    int ans = 0;
    for(int i = 1; i <= K; i ++)
        ans = (ans + mat.a[i][0]) % MOD;
    printf("%d\n", ans);
}
int main()
{
    prepare();
    while(scanf("%d%d", &N, &K) == 2, N || K)
        solve();
    return 0;    
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值