JZOJ 5990. Bear(斜线dp)

Description:

在这里插入图片描述
n<=12,m<=30

题解:

很容易想到一种一行一行的dp,我们觉得n太小了,想一列一列做,发现不行。

然后脑洞打开,直接斜着dp,发现就没有了。

Code:

#include<cstdio>
#include<cstring>
#define pp printf
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define fd(i, x, y) for(int i = x; i >= y; i --)
using namespace std;

const int N = 31;

int n, m, mo, a2[14];
int a[N * N][2], a0, b[N * N][2], b0, x, y;
int o, m2;
ll f[2][1 << 13], g[2][1 << 13];

#define w(x, y) ((x) >> (y) & 1)

int zz(int k, int d) {
	int nn = (x == n || y == 1) ? 0 : w(k, x) * a2[n];
	if(w(k, x)) k -= a2[x];
	if(w(k, n)) k -= a2[n];
	if(d == 1) k += a2[x - 1];
	if(d == 2) k += a2[x];
	return k + nn;
}

int main() {
	freopen("bear.in", "r", stdin);
	freopen("bear.out", "w", stdout);
	a2[0] = 1; fo(i, 1, 13) a2[i] = a2[i - 1] << 1;
	scanf("%d %d %d", &n, &m, &mo);
	fo(i, 1, n) a[++ a0][0] = i, a[a0][1] = 1;
	fo(j, 2, m) a[++ a0][0] = n, a[a0][1] = j;
	m2 = a2[n + 1] - 1;
	f[o][0] = 1;
	fo(i, 1, a0 - 1) {
		b0 = 0;
		x = a[i][0], y = a[i][1];
		
		while(x > 0 && y <= m)
			b[++ b0][0] = x, b[b0][1] = y, x --, y ++;
		fd(j, b0, 1) {
			memset(f[!o], 0, sizeof f[!o]);
			memset(g[!o], 0, sizeof g[!o]);
			x = b[j][0], y = b[j][1];
			fo(k, 0, m2) if(f[o][k]) {
				f[o][k] %= mo;
				g[o][k] %= mo;
				int z1 = w(k, n), z2 = y == m ? 1 : w(k, x - 1), z3 = x == n ? 1 : 0; 
				if(z1 || z2 && z3) { 
					int nk = zz(k, 0);
					f[!o][nk] += f[o][k] * 2;
					g[!o][nk] += g[o][k] * 2;
				} else
				if(z2 && !z3) {					
					int nk = zz(k, 2); 
					f[!o][nk] += f[o][k] * 2;
					g[!o][nk] += (g[o][k] + f[o][k]) * 2;
				} else
				if(!z2 && z3) {
					int nk = zz(k, 1);
					f[!o][nk] += f[o][k] * 2;
					g[!o][nk] += (g[o][k] + f[o][k]) * 2;
				} else {
					int nk = zz(k, 1);
					f[!o][nk] += f[o][k];
					g[!o][nk] += (g[o][k] + f[o][k]);
					nk = zz(k, 2);
					f[!o][nk] += f[o][k];
					g[!o][nk] += (g[o][k] + f[o][k]);
				}
			}
			o = !o;
		}
		memset(f[!o], 0, sizeof f[!o]);
		memset(g[!o], 0, sizeof g[!o]);
		x = a[i + 1][0], y = a[i + 1][1];
		while(x > 0 && y <= m) x --, y ++;
		int p = x;
		fo(k, 0, m2) if(f[o][k]) {
			int nk = k - w(k, p) * a2[p] + a2[n] * w(k, p);
			f[!o][nk] += f[o][k];
			g[!o][nk] += g[o][k];
		}
		o = !o;
	}
	ll ans = 0;
	fo(k, 0, m2) ans = (ans + g[o][k]) % mo;
	pp("%lld\n", ans * 2 % mo);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值