• 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
  • 相关阅读:
    c++笔试题3
    C++笔试题
    C++编程指南续(10-11)
    C++详解(8-9)
    C++编程指南(6-7)
    C++编程指南续(4-5)
    C++编程指南续
    C++的编程指南
    HPSocket介绍与使用
    WinForm中TreeView控件实现鼠标拖动节点(可实现同级节点位置互换,或拖到目标子节点)
  • 原文地址:https://www.cnblogs.com/galaxies/p/bzoj4417.html
Copyright © 2020-2023  润新知