luogu-P12152题解

前言

excat 给我推荐的题,好难!

题意

给定 \(n,m,V\),还有一个长为 \(m\) 的值域在 \([1,V]\) 中的整数序列 \(a\),再给定一个大小为 \(n \times (m+1)\) 的矩阵 \(c\)。定义一个整数序列是好的,当且仅当它的值域在 \([1,V]\) 中且所有值域在 \([1,V]\) 的长为 \(m\) 的整数序列都是它的子序列。定义一个好的整数序列 \(b\) 的价值为 \(\prod\limits_{i=1}^n c_{i,pre_i}\),其中 \(pre_i\)\(a\) 的最长前缀长度使得 \(a_{1 \sim pre_i}\)\(b_{1\sim i}\) 的一个子序列,若不存在则 \(pre_i = 0\)。求所有长度为 \(n\) 的好序列的价值和,答案对 \(10^9+7\) 取模。

题解

首先考虑一个序列是好的的条件,经过手玩可以发现貌似 \(mV\le n\) 就能构造出满足条件的序列,否则一定不行。考虑证明:

对于长 \(m\) 的序列,其每个位置 \(i\) 的取值为 \([1,V]\)。我们考虑构造一个长为 \(mV\) 的序列,其中每 \(V\) 个数分成一段,一段中依次填 \(1\sim V\) 即可。

考虑序列长度小于 \(mV\) 的时候,一定会有一个数 \(i\) 的出现次数小于 \(m\),这个由抽屉原理可得。显然这个序列就不包含子序列 \(A',A'={i,i,i,\dots,i}\),于是得证。

于是我们考虑对于段进行 dp,可以设 \(f_{i,j,k,p, q}\) 表示 \(b\) 序列填到了 \(i\),在 \(a\) 序列中已经匹配上前缀 \(j\),现在填完了 \(k\) 段,当前正在填的段已经有 \(p\) 种数是属于匹配过的,还有 \(q\) 种是没有匹配的数。这里状态看似是 \(\mathcal O(nm^2V^2)\) 的,但是注意当 \(n<mV\) 的时候是不合法的,所以我们不考虑这些状态,最后剩下的状态是 \(\mathcal O(n^3)\)

现在考虑 dp 转移,转移我们考虑两种,一种是往后多匹配一个数,还有一种是结束一个段。但是当你具体去写转移式子的时候会遇到问题,就是说有的地方进行转移的时候其系数我们无法确定,就是对于同一种状态它可能会有不同情况导致不同系数,所以我们需要区分。解决的办法是将不同的转移分开维护,于是我们重新定义一些辅助转移的数组,设 \(g_{i,j,l,p,q,0/1}\) 表示我们考虑往后匹配的转移时要求匹配 \(a_j\),并且是否存在某个数不能出现的限制。设 \(h_{i,j,k,p}\) 表示我们考虑结束一个段的转移时的 dp 值,设 \(z_{i,j,p}\) 表示我们已经完成所有匹配,并且已经有完整的 \(j\) 段,当前段出现了 \(p\) 种数。其他未定义的字母含义与 \(f\) 中含义相同。

此时我们就把 \(f\) 的转移拆成了四个 dp 互相转移,具体可以在枚举的时候依次处理 \(f,g,h,z\) 的转移。考虑 \(f\) 会贡献到 \(g,h\),如果需要对没有匹配的数确定顺序那么乘上对应系数即可,如果是 \(g\) 进行转移,考虑 \(g\) 能转移到 \(f,g,z\)\(g\) 自己的转移就是去讨论是否需要限制某个数不能出现,对于这个限制考虑一个数上次出现的位置与这次出现的位置之间是否已经间隔了 \(p\) 种数,这是可以预处理的。\(g\) 对于 \(f\) 也是同样可以直接贡献的。最后就是 \(g\) 可以把剩下需要匹配的数全部匹配完,然后就能够贡献到 \(z\),此时需要考虑是否新开一段,乘上对应系数即可。\(h\) 只能转移到 \(f,h\),同理分讨是否新开一段即可。\(z\) 不再赘述。

具体的式子其实是很简单的,可以自行尝试推理。这些转移都是 \(\mathcal O(1)\) 的,于是时间复杂度为 \(\mathcal O(n^3)\)

代码

inline int fd(int x, int y){return x * (v + 1) + y;}
inline pii get(int x){return make_pair(x / (v + 1), x % (v + 1));}
inline bool ok(int x, int y, int z){return id[x][y] <= z;}

signed main(){
    n = rd(), m = rd(); v = rd();
    if(m * v > n)return puts("0"), 0;
    for(int i = 1; i <= m; ++i)a[i] = rd();
    for(int i = 1; i <= n; ++i)
        for(int j = 0; j <= m; ++j)c[i][j] = rd();
    fac[0] = ifac[0] = pw[0] = s[n + 1] = 1;
    for(int i = 1; i <= n; ++i)fac[i] = Mul(fac[i - 1], i);
    for(int i = 1; i <= n; ++i)pw[i] = Mul(pw[i - 1], v);
    ifac[n] = Inv(fac[n]);
    for(int i = n - 1; i; --i)ifac[i] = Mul(ifac[i + 1], i + 1);
    for(int i = n; i; --i)s[i] = Mul(s[i + 1], c[i][m]);
    for(int i = 1; i <= v; ++i)id[0][i] = v + 1;
    for(int i = 1; i <= n; ++i){
        pos[i][1] = a[i]; for(int j = 1, k = 1; j <= v; ++j)
            if(pos[i - 1][j] != a[i])pos[i][++k] = pos[i - 1][j];
        for(int j = 1; j <= v; ++j)id[i][j] = v + 1;
        for(int j = 1; j <= v; ++j)
            if(pos[i][j])id[i][pos[i][j]] = j;
    }

    int res = 0, o = 0, nn = fd(m, v);
    f[0][fd(0, 0)][fd(0, 0)] = 1;
    for(int i = 1; i <= n; ++i, o ^= 1){
        for(int x = 0; x <= nn; ++x)for(int y = 0; y <= nn; ++y){
            auto p1 = get(x), p2 = get(y); int t = f[o][x][y]; f[o][x][y] = 0;
            int j = p1.first, p = p1.second, k = p2.first, q = p2.second;
            if(t){
                if(ok(j, a[j + 1], p))
                    ADD(g[o][x][y][1], t), ADD(h[o][fd(j, p + q)][k], Mul(t, fac[v - p]));
                else{
                    if(q)ADD(g[o][fd(j, p + 1)][fd(k, q - 1)][1], Mul(t, q));
                    ADD(h[o][fd(j, p + q)][k], Mul(Mul(t, q), fac[v - p - 1]));
                    ADD(g[o][fd(j, p + 1)][y][0], t);
                }
            }
            for(int w = 0; w < 2; ++w)if(t = g[o][x][y][w], g[o][x][y][w] = 0, t){
                if(p + q + 1 + w <= v)ADD(g[! o][x][fd(k, q + 1)][w], Mul(t, c[i][j]));
                ADD(g[! o][x][y][w], Mul(t, Mul(c[i][j], p + q - 1)));
                if(j + 1 < m)
                    if(p + q < v)ADD(f[! o][fd(j + 1, p)][y], Mul(t, c[i][j + 1]));
                    else ADD(f[! o][fd(j + 1, 0)][fd(k + 1, 0)], Mul(t, Mul(c[i][j + 1], fac[q])));
                else
                    if(p + q < v)ADD(z[! o][fd(k, p + q)], Mul(t, Mul(c[i][j + 1], fac[v - p])));
                    else ADD(z[! o][fd(k + 1, 0)], Mul(Mul(t, c[i][j + 1]), Mul(fac[v - p], fac[k + 1 == m ? 0 : v])));
            }
        }
        for(int x = 0; x <= nn; ++x)for(int k = 0; k <= m; ++k){
            auto p = get(x); int j = p.first, q = p.second, t = h[o][x][k]; h[o][x][k] = 0;
            if(t){
                if(q + 1 < v)ADD(h[! o][fd(j, q + 1)][k], Mul(t, c[i][j]));
                ADD(h[! o][x][k], Mul(t, Mul(c[i][j], q - 1)));
                if(q + 1 == v)ADD(f[! o][fd(j, 0)][fd(k + 1, 0)], Mul(t, c[i][j]));
            }
        }
        for(int x = 0; x <= nn; ++x){
            auto p = get(x); int j = p.first, q = p.second, t = z[o][x];
            if(! t)continue; z[o][x] = 0; if(j == m){
                ADD(res, Mul(Mul(t, pw[n - i + 1]), s[i])); continue;
            } ADD(z[! o][x], Mul(Mul(t, c[i][m]), q));
            if(q + 1 < v)ADD(z[! o][fd(j, q + 1)], Mul(t, c[i][m]));
            if(q + 1 == v)ADD(z[! o][fd(j + 1, 0)], Mul(Mul(t, c[i][m]), fac[j + 1 == m ? 0 : v]));
        }
    }
    return wt(Add(res, z[o][fd(m, 0)])), 0;
}
posted @ 2025-11-19 19:19  Lyrella  阅读(11)  评论(0)    收藏  举报