[SMOJ2192]跳跃

97 篇文章 0 订阅
4 篇文章 0 订阅

(这题早应该写题解了,但是好像一直没时间,拖了好久……)
首先,如果不考虑 bad 点,对于 n=0 的情况,应该怎么做呢?
比赛的时候,我想着可以用一个二维 dp 拿这部分的分,用滚动数组和前缀和优化一下,时间是三维的,空间是二维的,理论止应该可以通过 14 的测试点。
不过,因为前面都在搞前两题,这题到最后关头才来写,前缀和的地方有些细节没有处理好,所以到最后也没拿到那 20 分。

但是,事实上,如果不考虑 bad 点,并不需要二维的 dp,一维就可以了。
设想一个简化的问题:在长为 Tx x 轴上,从原点开始,每次向前跳 0 至 Mx 格,问恰好用 R 步跳到 Tx 的方案数。
fx[i][j] 表示跳了 i 步之后到达 j 的方案数,显然 fx[0][0]=1 ,并且有

fx[i][j]=k=max{jMx,0}j1fx[i1][k]

用前缀和优化一下,可以做到二维时间解决这个问题。至于纵向的 y 轴也是一样的道理,只是参数中 Tx 改为 Ty Mx 改为 My

对于有 bad 的情况,直接求合法方案是比较难算出的。我们很容易联想到一种逆向的思维,即用总的方案数减去非法方案数。
如果我们先尝试把 bad 点加在一维平面上呢?也就是说,不允许出现从某个 x1 跳到任意的 x1+badi 上。
可以发现,对于所有的非法方案,一定在跳的过程中若干步走了 bad 点。先考虑只有一个 bad 点的情况,不妨设 bad1=10
如果只跳了一次 bad 点,则可能是 0 跳到 10,1 跳到 11,……, Tx10 跳到 Tx
我们可以这样考虑,例如对于从 2 跳到 12 的情况,可以认为把这个区间直接从数轴上去掉,只剩下 0 到 2 和 12 到 Tx (Tx10) 个点,那我们就变成了用 (R1) 步跳完这个剩下的区间,可以发现这是一个子问题,已经在 fx 数组中有答案了。把这些不合法答案减去即可。
但是很快就会发现,第一步、第二步都跳了 bad 点的情况,被减去两次,应该加回去。之后多加了,再减去一些。依此类推,其实就是容斥的过程。
另外,可以发现,跳了 k 步(例如在第一步或第二步)bad 点的情况其实是一致的,因此没有必要枚举具体哪几步跳了 bad ,可以直接乘上 CkR

而如果有多个 bad 点,则不合法情况可能是多个 badi 之和。例如,对于 bad={10,20,30} ,从 x1 跳到 x1+30 的非法情况,可能是直接跳过去的,也有可能先从 x1 跳到 x1+10 ,再跳到 x1+30 。这样,在计算从 x1 跳到 x1+k×10 的时候,就要考虑有多少种组成 k 的情况。可预先对 bad 数组做一个背包算出。

现在,把这个问题推广到二维平面上,其实并没有变复杂多少。
从 (0,0) 出发,跳到 k 步到达点 (i,j) 时,可以认为在 x 方向上跳 k 步到达 i ,在 y 方向上跳 k 步到达 j,而具体怎么跳我们不关注,每一种 x 方向上的方案都可以搭配 y 方向上的一种方案,因此根据乘法原理, fx[k][i]×fy[k][j] 即可算出总的方案数。

计算不合法的情况的情况时,同样用这种思想,枚举所跳的 bad 值(若干个 bad 点之和),将该种情况的 fx fy 相乘,再乘上 CkR 以考虑在不同的步跳 bad 点的情况,再乘上用若干 badi 组合成这种 bad 值的方案数,相应做一下容斥,就可以了。

参考代码:

#nclude <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>

using namespace std;

#define sign(x) (x&1?-1:1)

const long long MAXN = 50   + 100;
const long long MAXR = 1600 + 100;
const long long MAXL = 800  + 100;
const long long MOD = 10007;

long long Tx, Ty, Mx, My, R, n;
long long bad[MAXN];

long long prefix_sum[MAXL][2];
long long fx[MAXR][MAXL], fy[MAXR][MAXL], g[MAXR][MAXR], C[MAXR][MAXR];

int main(void) {
    freopen("2192.in", "r", stdin);
    freopen("2192.out", "w", stdout);
    scanf("%lld%lld%lld%lld%lld%lld", &Tx, &Ty, &Mx, &My, &R, &n); long long L = min(Tx, Ty);
    for (long long i = 1; i <= n; i++) scanf("%lld", &bad[i]); bad[++n] = 0LL;
    /*
    fx[0][0] = 1;
    for (long long i = 1; i <= R; i++)
        for (long long j = 0; j <= Tx; j++)
            for (long long kb = 0; kb <= min(Mx, j); kb++) (fx[i][j] += fx[i - 1][j - kb]) %= MOD;
    fy[0][0] = 1;
    for (long long i = 1; i <= R; i++)
        for (long long j = 0; j <= Ty; j++)
            for (long long kb = 0; kb <= min(My, j); kb++) (fy[i][j] += fy[i - 1][j - kb]) %= MOD;
    */
    fx[0][0] = 1LL;
    for (long long i = 0; i <= Tx; i++) prefix_sum[i][0] = 1LL;
    for (long long i = 1; i <= R; i++) {
        prefix_sum[0][i & 1] = 1LL;
        for (long long j = 0; j <= Tx; j++) {
            (fx[i][j] += (prefix_sum[j][(i & 1) ^ 1] - (j > Mx ? prefix_sum[j - Mx - 1][(i & 1) ^ 1] : 0LL)) % MOD + MOD) %= MOD;
            if (j) prefix_sum[j][i & 1] = (prefix_sum[j - 1][i & 1] + fx[i][j]) % MOD;
//          printf("%d ", fx[i][j]);
        }
//      putchar('\n');
    }

//  memset(prefix_sum, 0, sizeof prefix_sum);
    fy[0][0] = 1LL;
    for (long long i = 0; i <= Ty; i++) prefix_sum[i][0] = 1LL;
    for (long long i = 1; i <= R; i++) {
        prefix_sum[0][i & 1] = 1LL;
        for (long long j = 0; j <= Ty; j++) {
            (fy[i][j] += (prefix_sum[j][(i & 1) ^ 1] - (j > My ? prefix_sum[j - My - 1][(i & 1) ^ 1] : 0LL)) % MOD + MOD) %= MOD;
            if (j) prefix_sum[j][i & 1] = (prefix_sum[j - 1][i & 1] + fy[i][j]) % MOD;
//          printf("%d ", fy[i][j]);
        }
//      putchar('\n');
    }


    g[0][0] = 1LL;
    for (long long i = 1; i <= R; i++) {
        for (long long j = 0; j * 10 <= L + 1; j++) {
            for (long long k = 1; k <= n; k++)
                if (j * 10 >= bad[k]) (g[i][j] += g[i - 1][j - bad[k] / 10]) %= MOD;
//          printf("%d ", g[i][j]);
        }
//      putchar('\n');
    }

    C[0][0] = 1LL;
    for (long long i = 1; i <= R; i++) {
        C[i][0] = C[i][i] = 1;
        for (long long j = 1; j < i; j++) C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % MOD;
    }

    long long ans = 0LL;// printf("%d\n", ans);
    for (long long i = 0; i <= R; i++)
        for (long long j = 0; j * 10 <= L; j++)
            (ans += (sign(i) * (g[i][j] * fx[R - i][Tx - j * 10] % MOD * fy[R - i][Ty - j * 10] % MOD * C[R][i] % MOD) % MOD) + MOD) %= MOD;

    printf("%lld\n", ans);
    return 0;
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值