2020CCPC秦皇岛 H. Holy Sequence

考虑每个数字 \(x\) 第一次出现在位置 \(pos\) 的贡献, 那么就要计算位置 \(pos\) 前后的合法序列的数量了.

定义这两个 \(DP\) 数组:

  • \(pre[i][j]\): 当 \(n=i\) 时, \(p_i=j\) 的合法序列数.
  • \(suff[i][j]\): \(a_1=j\) (无视 \(a_0\) 的限制) 长度为 \(i\) 的合法序列数.

\(suff\) 数组的推导可以考虑 \(a_2\)\(a_1\) 的大小关系. \(a_2\leq a_1\) 时, 去掉 \(a_2\)\(suff[i-1][j]\); \(a_2=a_1+1\) 时, 去掉 \(a_1\), 即 \(suff[i-1][j+1]\).

然后我们考虑平方怎么处理. 对于 \(x\) 出现了 \(k\) 次的序列, 我们相当于可重复地选两次 \(x\), 这样贡献也是 \(k^2\).

现在枚举每个数字 \(i\) 第一次出现在位置 \(j\) 的贡献, 前缀的方案数是 \(pre[i-1][j-1]\), 后缀由于要选择两次 \(x\), 分四类考虑:

  1. 两次都选择位置 \(j\).
  2. 两次选择位置相同但不是 \(j\).
  3. 两次中一次选择 \(j\), 一次不选 \(j\).
  4. 两次选择位置不同且都不选择 \(j\).

选择后面有 \(x=0\) ~ \(2\)\(i\) 时, 相当于这个位置确定下来了, 对前后取数没有影响, 可以直接去掉, 剩的位置方案即以 \(i\) 开头, 长度为 \(n-j+1-x\): \(suff[n-j+1-x][i]\) 乘以选择位置的排列数.

代码

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define inc(x, l, r) for (int x = l; x <= r; x++)

const int N = 3e3;

int t, n, m;
ll pre[N + 5][N + 5], suff[N + 5][N + 5];
// pre[pos][num]    suff[len][num]

int main() {
    cin >> t;
    inc(p, 1, t) {
        cin >> n >> m;

        pre[0][0] = 1;
        inc(i, 1, n) {
            inc(j, 1, i) {
                pre[i][j] = (pre[i - 1][j] * j + pre[i - 1][j - 1]) % m;
            }
        }

        inc(i, 1, n) suff[1][i] = 1;
        inc(i, 2, n) {
            inc(j, 1, n + 1 - i) {
                suff[i][j] = (suff[i - 1][j] * j + suff[i - 1][j + 1]) % m;
            }
        }

        vector<ll> res(n + 1);
        inc(i, 1, n) {                        // num
            inc(j, i, n) {                    //    pos
                ll tmp = suff[n - j + 1][i];  //情况一
                if (j < n)
                    tmp =
                        (tmp + 3 * (n - j) * suff[n - j][i]) % m;  //情况二, 三
                if (j < n - 1)
                    tmp =
                        (tmp + (n - j) * (n - j - 1) % m * suff[n - j - 1][i]) %
                        m;  //情况四
                res[i] = (res[i] + pre[j - 1][i - 1] * tmp) % m;
            }
        }

        cout << "Case #" << p << ":\n";
        inc(i, 1, n) {
            cout << res[i] << " \n"[i == n];
        }
    }
}

posted @ 2020-10-22 20:34  Linqi05  阅读(415)  评论(1编辑  收藏  举报