首页 > 代码库 > bzoj4417 [Shoi2013]超级跳马

bzoj4417 [Shoi2013]超级跳马

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4417

【题解】

令f[i,j]表示到第2i-1列第j行的方案数,g[i,j]表示到第2i行第j列的方案数。

那么有

f[i,j]=Σg[1..i-1,j]+Σg[1..i-1,j-1]+Σg[1..i-1,j+1]

g[i,j]=Σf[1..i,j]+Σf[1..i,j-1]+Σf[1..i,j+1]

将f[i,j]和g[i,j]重新表示为前缀和形式

f[i,j]=f[i-1,j]+g[i-1,j]+g[i-1,j-1]+g[i-1,j+1]

g[i,j]=g[i-1,j]+f[i,j]+f[i,j-1]+f[i,j+1]

容易想到矩阵乘法

按照这种形式排列

f[i,1], f[i,2], ..., f[i,n], g[i,1], g[i,2], ..., g[i,n]

那么g转移要用到f怎么办?

我们想,让这个矩阵连续乘2个转移矩阵

第一个转移完f和g的前缀和部分。到乘第二个矩阵的时候,f已经转移完了,就能用f的信息了。

真正矩阵乘法把两个转移矩阵乘在一起做就行了。

要拓宽思路不要想着一个转移矩阵搞定问题。

注意判断m是奇偶的问题,以及n=1和不用做矩阵乘法等特判

技术分享
# include <stdio.h>
# include <assert.h>
# include <string.h>
# include <iostream>
# include <algorithm>
// # include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int M = 150 + 10;
const int mod = 30011;

# define RG register
# define ST static

struct matrix {
    int a[M][M], n, m;
    inline void set(int _n, int _m) {
        n = _n, m = _m;
        memset(a, 0, sizeof a);
    }
    friend matrix operator *(matrix a, matrix b) {
        matrix c; c.set(a.n, b.m);
        assert(a.m == b.n);
        for (int i=1; i<=c.n; ++i)
            for (int j=1; j<=c.m; ++j) 
                for (int k=1; k<=a.m; ++k)
                    c.a[i][j] = (c.a[i][j] + a.a[i][k] * b.a[k][j]) % mod;
        return c;
    }
    friend matrix operator ^(matrix a, int b) {
        matrix c; c.set(a.n, a.m);
        assert(a.n == a.m);
        for (int i=1; i<=c.n; ++i) c.a[i][i] = 1;
        while(b) {
            if(b&1) c = c*a;
            a = a*a;
            b >>= 1;
        }
        return c;
    }
    inline void prt() {
        for (int i=1; i<=n; ++i, cout << endl)
            for (int j=1; j<=m; ++j) cout << a[i][j] << " ";
    }
}A, T1, T2, T, B;

int n, m;

int main() {
    cin >> n >> m;
    T1.set(n+n, n+n), T2.set(n+n, n+n);
    for (int i=1; i<=n; ++i) {
        T1.a[i][i] = 1;
        T2.a[i][i] = 1;
        T1.a[i+n][i] = 1;
        T2.a[i][i+n] = 1;
        if(i != 1) {
            T1.a[i-1+n][i] = 1;
            T2.a[i-1][i+n] = 1;
        }
        if(i != n) {
            T1.a[i+1+n][i] = 1;
            T2.a[i+1][i+n] = 1;
        }
        T1.a[i+n][i+n] = 1;
        T2.a[i+n][i+n] = 1;
    }
    T = T1*T2;
    A.set(1, n+n);
    A.a[1][1] = 1;
    A.a[1][1+n] = 1;
    if(n != 1) A.a[1][1+n+1] =1;
    int times = (m-1)/2;
    if(times == 0) {
        if(m == 1) cout << A.a[1][n];
        else cout << A.a[1][n+n];
        return 0;
    }
    --times;
    A = A * (T^times);
    B = A * T;
    if(m&1) cout << ((B.a[1][n] - A.a[1][n]) % mod + mod) % mod;
    else cout << ((B.a[1][n+n] - A.a[1][n+n]) % mod + mod) % mod;
    return 0;    
}
View Code

 

bzoj4417 [Shoi2013]超级跳马