HDOJ 2604 Queuing 解题报告

    A了这一题,学了不少东西,说说。

    首先,题意是计算a[L]%m的值。而a[1]=2,a[2]=4,a[3]=6,a[4]=9。L的范围比较夸张,[0,1000000]。

    E-queues串的结尾必然是[1]:000,[2]:100,[3]:001,[4]:011,[5]:010,[6]:110

    在长度为n-1的E-queues串加0或1,则

    a[n][1]=a[n-1][1]+a[n-1][2]

    a[n][2]=a[n-1][5]+a[n-1][6]

    a[n][3]=a[n-1][1]+a[n-1][2]

    a[n][4]=a[n-1][3]

    a[n][5]=a[n-1][3]

    a[n][3]=a[n-1][4]

    按照这种方式,可以得到:a[n]=a[n-1]+a[n-3]+a[n-4]

    得到递推公式,一般来说就可以解决问题了,可是题目给的L范围比较大。。。直接算a[n]几乎不可能,很容易超时。

    笔者想到这里就卡住了,就在网上找了一下大牛的方法,发现都是用 二分快速幂 来做的。

    矩阵是大一的时候学习的,至今没怎么用过,没想到今天派上了用场。

    二分快速幂,说的简单一点,就是充分利用每一次的运算结果。例如计算A的n次方:

res=1;
while(n)
{
    if(n&1)
        res=res*A;
    n>>=1;
    A=A*A;
}

    常规的方法要计算n次,而二分的话,就可以只计算log2 n次。

    或者说n=15,res=A^n=(A^8)*(A^4)*(A^2)*(A^1)。每一项都是下一项的平方,这就是2分的思想。

    那这道题呢?我们可以构建一个矩阵的式子:

    [ a[n], a[n-1], a[n-2], a[n-3] ] = [ a[n-1], a[n-2], a[n-3], a[n-4] ]*[ [1, 0, 1, 1]T, [ 1, 0, 0, 0 ]T, [ 0, 1, 0, 0 ]T, [ 0, 0, 1, 0 ]T ]

    额。。。确实很难看,笔者也没办法,意思就是这个意思。T是转置的意思。

    根据递推式,可以得到:

    [ a[n], a[n-1], a[n-2], a[n-3] ] = [ a[4], a[3], a[2], a[1] ]*[ [1, 0, 1, 1]T, [ 1, 0, 0, 0 ]T, [ 0, 1, 0, 0 ]T, [ 0, 0, 1, 0 ]T ]^(n-4)

    对矩阵n-4进行二分快速幂的运算,运算过程中模m,可以很快得到结果。下面是笔者的AC代码:

#include <iostream>
using namespace std;

const int LEN=4;
int m;

struct Matrix
{
    int matrix[LEN][LEN];
} original,result;

Matrix mul(Matrix &a, Matrix &b)
{
    int i,j,k;
    Matrix c;
    memset(c.matrix,0,LEN*LEN*4);
    for(i=0;i<LEN;i++)
        for(j=0;j<LEN;j++)
            for(k=0;k<LEN;k++)
            {
                c.matrix[i][j]+=a.matrix[i][k]*b.matrix[k][j];
                c.matrix[i][j]%=m;
            }
    return c;
}

int main()
{
    int i,n,sum;
    int s[5]={9,6,4,2,0};
    while(cin>>n>>m)
    {
        if(n<5)
            cout<<s[4-n]%m<<endl;
        else
        {
            sum=0;
            n-=4;
            memset(original.matrix,0,LEN*LEN*4);
            memset(result.matrix,0,LEN*LEN*4);
            for(i=0;i<LEN;i++)
                original.matrix[i][(i+1)%4]=result.matrix[i][i]=1;
            original.matrix[0][0]=original.matrix[2][0]=1;
            while(n)
            {
                if(n&1)
                    result=mul(result,original);
                n>>=1;
                original=mul(original,original);
            }
            for(i=0;i<LEN;i++)
            {
                sum+=result.matrix[i][0]*s[i];
                sum%=m;
            }
            cout<<sum<<endl;
        }
    }
}

    哈哈,IT小男孩原创~http://www.cnblogs.com/IT-BOY

转载于:https://www.cnblogs.com/IT-BOY/archive/2013/02/03/2890841.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值