[矩阵快速幂]hdu2604 Queuing

hdu2604 Queuing

题意:
f代表女性,m代表男性,排成纵队,长度为L的队列方案数有2^L种,如果某个队列存在"fmf"或"fff"子队列,那么这个队列是O-queue,否则是E-queue
求E-queue的数目%MOD后的值
思路:
用f(n)表示n长度的E序列数目,此题倒推
如果最后一位是m,前面无限制,求f(n-1)即可
如果最后一位是f,前面有限制,往前推一位,mf ff,这两个都有限制,再往前推一位,fmf mmf fff mff,其中fmf和fff不符合条件我们不考虑,
如果是mmf,前面无限制,求f(n-3)即可,
如果是mff,前面有限制,往前推一位,fmff mmff,fmff不符合条件我们不考虑,mmff前面无限制,求f(n-4)即可
至此,对于当前的f(n)来说,所有情况都已考虑到,我们可以写出式子:
f(n) = f(n-1) + f(n-3) + f(n-4)
显然不能直接递归,时间会跪,所以我们转化成矩阵


看大神说也可以用trie图表示,有字符串fmf和fff构建trie图


假定根节点为字符串的最后一个字符,由根结点到根节点的回路有m、fmm、ffmm,注意这是从一个字符串的后面向前走这些字符到达另一个字符串,恰好和上面的扩展递推相一致

这个图还是比较迷的……

代码:

/**************************************************************
    Problem: HDU_2604
    User: soundwave
    Language: C++
    Result: Accepted
    Time: 280ms
    Memory: 1576KB
****************************************************************/
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <iostream>
#include <stdio.h>
#include <vector>
/*
感觉这个推公式还是比较麻烦的,因为还有许多重复项
所以还是推出关系式,用矩阵乘法求比较好
f(n) = f(n-1) + f(n-3) + f(n-4);
A^
*/
using namespace std;
typedef vector<int>Vint;
typedef vector<Vint>VVint;
typedef __int64 LL;
int MOD;
//矩阵乘法
VVint calc(VVint &A, VVint &B){
    VVint C(A.size(), Vint(A.size()));
    for(int i=0; i<A.size(); i++)
    for(int j=0; j<B[0].size(); j++)
    for(int k=0; k<B.size(); k++)
        C[i][j] = (C[i][j] + (A[i][k]*B[k][j])%MOD) % MOD;
    return C;
}
//二分快速幂
VVint my_pow(VVint &A, int c){
    VVint B(A.size(), Vint(A.size()));
    for(int i=0; i<A.size(); i++)
        B[i][i] = 1;
    if(c==1) return A;
    while(c>0){
        if(c&1) B = calc(B,A);
        A = calc(A,A);
        c>>=1;
    }
    return B;
}
int main(){
    int k;
    while(~scanf("%d%d", &k, &MOD)){
        VVint A(4, Vint(4));
        int B[5] = {0,2,4,6,9};
        if(k<5){
            printf("%d\n", B[k]%MOD);
            continue;
        }
        A[0][0] = A[0][2] = A[0][3] = 1;
        A[1][0] = A[2][1] = A[3][2] = 1;
        A = my_pow(A,k-4);
        int re = 0;
        for(int i=0; i<4; i++)
            re = (re + (A[0][i]*B[4-i])%MOD) % MOD;
        printf("%d\n", re);
    }
    return 0;
}
/*
0 1 2 3 4
0 2 4 6 9
----------
0 0
Null
1 2
f m
2 4
ff mm fm mf
3 6
[fff] ffm [fmf] fmm mff mfm mmf mmm
4 9
[ffff] [fffm] [ffmf] ffmm
[fmff] [fmfm] fmmf fmmm
[mfff] mffm [mfmf] mfmm
mmff mmfm mmmf mmmm
5 2^5-17=15
[fffff] [ffffm] [fffmf] [fffmm]
[ffmff] [ffmfm] [ffmmf] ffmmm
[fmfff] [fmffm] [fmfmf] [fmfmm]
fmmff fmmfm fmmmf fmmmm
[mffff] [mfffm] mffmf mffmm
[mfmff] [mfmfm] mfmmf mfmmm
[mmfff] mmffm [mmfmf] mmfmm
mmmff mmmfm mmmmf mmmmm
*/


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值