BZOJ 4417 [Shoi2013]超级跳马

首先,\(dp[i][j]\)表示到\(i\)\(j\)列的方案数,则显然可以写出状态转移方程:

\[dp[i][j] = \sum_{k = 1}^{(m + 1) / 2} dp[2 * k - 1][j - 1] + dp[2 * k - 1][j] + dp[2 * k - 1][j + 1] \]

\(2 * k - 1\)这个东西不是很优美啊(这玩意怎么构造矩阵……),考虑把奇数\(i\)和偶数\(i\)分开考虑,\(dp[0][i][j]\)为到\(2 * i\)\(j\)列的方案数,\(dp[1][i][j]\)为到\(2 * i - 1\)\(j\)列的方案数。那么,就得到了两个更清真的(转移来自的状态是连续的,而不是一个隔一个)的状态转移方程:

\[dp[0][i][j] = \sum_{k = 1}^{(m + 1) / 2} dp[1][k][j - 1] + dp[1][k][j] + dp[1][k][j + 1] \]

\[dp[1][i][j] = \sum_{k = 1}^{(m + 1) / 2} dp[0][k - 1][j - 1] + dp[0][k - 1][j] + dp[0][k - 1][j + 1] \]

这个东西还是很难变成矩阵,因为显然矩阵开不了\(m\)那么大……

发现等式右边就是一个前缀和嘛!那么,设

\[f[0/1][i][j] = \sum_{k = 1}^{i} dp[0/1][k][j] \]

那么有:

\[f[1][i][j] = f[1][i - 1][j] + f[0][i - 1][j - 1] + f[0][i - 1][j] + f[0][i - 1][j + 1] \]

\[f0][i][j] = f[0][i - 1][j] + f[1][i][j - 1] + f[1][i][j] + f[1][i][j + 1] \]

这个就可以构造矩阵了!

#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
#define enter putchar('\n')
#define space putchar(' ')
template <class T>
void read(T &x){
    char c;
    bool op = 0;
    while(c = getchar(), c > '9' || c < '0')
	if(c == '-') op = 1;
    x = c - '0';
    while(c = getchar(), c >= '0' && c <= '9')
	x = x * 10 + c - '0';
    if(op) x = -x;
}
template <class T>
void write(T x){
    if(x < 0) putchar('-'), x = -x;
    if(x >= 10) write(x / 10);
    putchar('0' + x % 10);
}

const int N = 102, P = 30011;
int n, m;
struct matrix {
    int g[N][N];
    matrix(){
	memset(g, 0, sizeof(g));
    }
    matrix(int useless){
	for(int i = 0; i < n; i++)
	    for(int j = 0; j < n; j++)
		g[i][j] = (i == j);
    }
    matrix operator * (const matrix &b){
	matrix c;
	for(int k = 0; k < n; k++)
	    for(int i = 0; i < n; i++)
		for(int j = 0; j < n; j++)
		    c.g[i][j] = (c.g[i][j] + g[i][k] * b.g[k][j]) % P;
	return c;
    }
    void out(){
	for(int i = 0; i < n; i++)
	    for(int j = 0; j < n; j++)
		write(g[i][j]), j == n - 1 ? enter: space;
	enter;
    }
} ans, op1, op2;

matrix qpow(matrix a, int x){
    matrix ret(1);
    while(x){
	if(x & 1) ret = ret * a;
	a = a * a;
	x >>= 1;
    }
    return ret;
}
int mod(int x){
    return (x % P + P) % P;
}

int main(){

    read(n), read(m);
    ans.g[0][0] = 1;
    for(int i = 0; i < n; i++){
	if(i) op1.g[i + n][i - 1] = op2.g[i][i + n - 1] = 1;
	op1.g[i + n][i] = op1.g[i + n][i + n] = op2.g[i][i + n] = op2.g[i][i] = 1;
	if(i + 1 < n) op1.g[i + n][i + 1] = op2.g[i][i + n + 1] = 1;
	op1.g[i][i] = op2.g[i + n][i + n] = 1;
    }
    n *= 2;
    ans = qpow(op2 * op1, m / 2 - 1) * ans;
    if(m & 1) write(mod((op2 * op1 * ans).g[n / 2 - 1][0] - ans.g[n / 2 - 1][0])), enter;
    else write(mod((op2 * op1 * ans).g[n - 1][0] - ans.g[n - 1][0])), enter;

    return 0;
}
posted @ 2017-12-20 16:45  胡小兔  阅读(190)  评论(0编辑  收藏  举报