3.18考试总结

T1

T2意义不大,T3我单独拎出来说说 \(\mathrm{2-sat}\)

大意:求有多少n排列满足不存在长度超过 \(m\) 的以1/-1为公差的等差数列。\(n,m \le 200\)

这道题的套路是:把排列按照题目规则划分为若干段,并且记录每一段中元素相对排名(而不是具体值)

这样就可以很方便的插入,添加一个新的段,最终构造出长度为 \(n\) 的序列,它就是一个排列。

放到这道题上,就是:对于任意一个排列,我们按照极长的等差数列将其划分为若干段。

\(dp_{i,j}\) 表示目前构成的序列总长是 \(i\) 然后已经有了 \(j\) 段的方案数。

如果不考虑相邻段可以构成一个大段,那么有如下转移:

\(dp_{i,j}+=dp_{x,j-1} \times j \times (1+[i>x+1])\)

其中乘 \(j\) 表示最新的这一段所处的值域在所有段中的相对排名,后面那个表示如果这个段长度大于 \(1\) 那么就可以正反两种放法

问题在于,一段可能会被你拆开两次进行计数。所以设计一个容斥系数来干掉它。

这个容斥系数显然只与你整个段的长度有关。

\(dp_{i,j}+=dp_{x,j-1} \times j \times (1+[i>x+1]) \times f_{i-x}\)

\(f(i) = -\displaystyle \sum_{j=1}^{m}f(i-j)\)

就是枚举最后一个子段的长度,那么如果有 \(T\) 个子段就会被计算 \((-1)^{T-1}\) 次。容斥完全。

时间复杂度 \(O(n^3)\)


以上是题解做法,但是没看懂,希望有看懂的来讲讲

下面是 \(\color {black} g \color {red} {vihvo}\) 的做法(注意:是考场场切加强版做法!!!

首先我们考虑,对于一个合法序列,我们一定可以把它变成若干段等差数列,这段等差数列的长度最短可能是 \(1\),最长也能是 \(m\),但是它一定是这样的。

然后捏?

我们考虑如果我们现在有一段等差数列,原序列我们就是会有一个一个一个一个(突然恶臭)的序列放在一起,然后我们考虑其实此时我们有三种处理方式

  • 把我当前的这段放在中间,剩下的两段放在两边接起来
  • 把我当前的这段放在接在其中一段得到后面
  • 把我当前这段直接放成一个单独序列,两边都不连哈哈

对于这三种情况我们可能会有疑惑,假如说我当前序列是 \(3,4\) 我要是接在 \(1,2\) 的后面不就凉了吗

但是我们可以换一种方式想,如果我真接在后面了,不管是否超越长度,它都会导致算重或算错

但是我直接把这种情况可以忽略掉,原因是当前的这个长长的序列,我们有一种非常美妙的方式解决,就是我们直接枚举 \(1,2,3,4\) 那么我们发现刚好可以替换掉我们当前的这一段

所以我们接的时候就不要让你的当前数列能和其它的数列拼成新的等差数列,因为早晚也会出现那个数列,我直接枚举的话就可以防止方案算重

这三种情况我们直接刷表,大力分讨,列出DP方程就能求解了

具体的话先枚举 \(i\) 表示这一段的开头是哪个数,然后枚举 \(j\) 表示现在已经有了 \(j\) 段,最后我们枚举 \(k\) 表示我枚举的长度,显然上界是 \(\min (n-i,m)\),而且当 \(k=1\) 时需要特判一下,原因是只有一个元素的话如果空了出来,两边都可以和它拼,所以要特判

Fr (i, 0, n) Fr (j, 0, i) Fr (k, 1, n - i) {
    if (k > mn[i + 1][i + k]) break;
    if (k == 1) {
        ll sum = (F[i][j][0] + F[i][j][1] + F[i][j][2]) % MD;
        Add(F[i + k][j + 1][2], sum);
        Add(F[i + k][j][1], (F[i][j][0] * j * 2 + F[i][j][1] * (j * 2 - 1) +
                             F[i][j][2] * (j * 2 - 2)) % MD);
        if (j > 1) Add(F[i + k][j - 1][0], (F[i][j][0] * A(j) +
                                            F[i][j][1] * (A(j) - (j - 1)) + F[i][j][2] * A(j - 1)) % MD);
    } else {
        ll sum = (F[i][j][0] + F[i][j][1] + F[i][j][2]) % MD;
        // 单独开一个段
        Add(F[i + k][j + 1][1], sum * 2 % MD);
        // 贴上去
        Add(F[i + k][j][0], sum * j * 2 % MD);
        Add(F[i + k][j][1], (F[i][j][0] * j * 2 + F[i][j][1] * (j * 2 - 1) +
                             F[i][j][2] * (j * 2 - 2)) % MD);
        // 把两个连起来
        if (j > 1) Add(F[i + k][j - 1][0], (F[i][j][0] * A(j) * 2 + 
                                            F[i][j][1] * (A(j) * 2 - (j - 1)) +
                                            F[i][j][2] * (A(j) - (j - 1)) * 2) % MD);
    }
}
posted @ 2022-03-18 20:02  RevolutionBP  阅读(46)  评论(0编辑  收藏  举报