嘘~ 正在从服务器偷取页面 . . .

[AGC013D] Piling Up 题解


[AGC013D] Piling Up 题解

题目链接:[AGC013D] Piling Up

题意

一开始有 $n$ 个颜色为黑白的球,但不知道黑白色分别有多少。

$m$ 次操作,每次先拿出一个球,再放入黑白球各一个,再拿出一个球,

最后拿出的球按顺序排列会形成一个颜色序列,求颜色序列有多少种。

答案对 $10^9+7$ 取模。

数据范围

$1 \le n, m \le 3000$ 。

考虑设黑球初始有 $x_0$ 个,则白球有 $n - x_0$ 个。

记 $x_i$ 为 $i$ 次操作后的黑球数量,考察 $i+1$ 的每种情况:

  1. 两次都取黑球,要求 $x_i > 0$ ,有 $x_{i+1} = x_i -1$ 。
  2. 先黑球后白球,要求 $x_i > 0$ ,有 $x_{i+1} = x_i$ 。
  3. 先白球后黑球,要求 $x_i < n$ ,有 $x_{i+1}=x_i$ 。
  4. 两次都取白球,要求 $x_i < n$ ,有 $x_{i+1} = x_i + 1$ 。

那么对于一种操作序列,根据以上关系式,我们可以确定 $x_0$ 的区间。

考虑设 $f(i,j)$ 为 $x_i=j$ 时前 $i$ 次操作的合法方案数。

初始 $f(0,j) = 1$ ,答案为 $\sum f(n,j)$ 。转移显然。

不过这样的 dp 是会算重的,当 $x_0$ 的合法区间长度不为 $1$ 时,就可能统计到其他的情况。

考虑重新设 $f(i,j)$ 为 $x_i = j$ 时前 $i$ 次操作的合法方案数,要求 $j$ 没有达到过 $0$ 。

同时设 $g(i,j)$ 为 $x_i = j$ 时前前 $i$ 次操作的合法方案数,不过要求 $j$ 至少达到过一次 $0$ 。

答案就是 $\sum g(n,j)$ 。这样我们对于每个区间只统计了一次,于是这题就做完了

时间复杂度 $\mathcal{O}(nm)$

代码:

#include <bits/stdc++.h>
using namespace std;
// #define int long long
#define INF 0x3f3f3f3f3f3f3f3f
typedef long long ll;
const int mod = 1e9 + 7;
template<typename T> void up(T &x, T y) { x < y ? x = y : 0; }
template<typename T> void down(T &x, T y) { x > y ? x = y : 0; }
template<typename T> void add(T &x, T y) { (x += y) >= mod ? x -= mod : 0; }
template<typename T> T Add(T x, T y) { return (x + y) >= mod ? x + y - mod : x + y; }
#define rep(i, a, b) for(int i = (a), i##END = (b); i <= i##END; i++)
#define Rep(i, a, b) for(int i = (a), i##END = (b); i >= i##END; i--)
#define N ((int)(3e3 + 15))

int n, m; ll res, f[2][N], g[2][N];
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n >> m; g[0][0] = 1;
    rep(i, 1, n) f[0][i] = 1;
    rep(i, 1, m)
    {
        const int cur = i & 1, pre = cur ^ 1;
        rep(j, 0, n) f[cur][j] = g[cur][j] = 0;
        rep(j, 0, n)
        {
            if(j)
            {
                add(f[cur][j], f[pre][j - 1]);
                add(g[cur][j], Add(g[pre][j - 1], g[pre][j]));
                if(j == 1) add(g[cur][j], f[pre][j]);
                else add(f[cur][j], f[pre][j]);
            }
            if(j < n)
            {
                add(g[cur][j], Add(g[pre][j + 1], g[pre][j]));
                if(!j) add(g[cur][j], f[pre][j + 1]);
                else add(f[cur][j], Add(f[pre][j + 1], f[pre][j]));
            }
        }
    }
    rep(j, 0, n) add(res, g[m & 1][j]);
    cout << res << '\n';
    return 0;
}

参考文献

[1] https://www.luogu.com.cn/article/cy68zxln

[2] https://www.luogu.com.cn/article/rxlwqm73


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录