XXS0103 局部函数

XXS0103 局部函数

记 \(U=\{0,1,2,3\}\)。现有 \(n\) 个变量 \(x_i \in U\),初始 \(x_i=a_i\) 且 \(x_0=x_{n+1}=0\)。另给定 \(n\) 个二元函数 \(g_i : U^2 \to U\)。

考虑 \(1\sim n\) 的排列 \(p_i\),对 \(i=1,2,\cdots,n\) 依次执行如下操作:

  • 记 \(k=p_i\),令 \(x_k \gets g_k(x_{k-1},x_{k+1})\)。

对所有排列 \(p\),求可得 \(x_1 \sim x_n\) 最终序列的数目。取模 \(998244353\)。

\(n \le 2 \times 10^5\),2 秒,512 MB。

一个基本的刻画是,考虑相邻项 \(u,v\),若 \(x_u\) 的更新时刻早于 \(x_v\) 则连边 \(u \to v\),建图是有向链。

取任意拓扑序均可得到唯一的 \(x_1 \sim x_n\)。考虑 \(O(4^n)\) 枚举 \(x_1 \sim x_n\),根据上述刻画设计内层 DP:

  • 设 \(f_{0/1,i}\) 为满足下列条件时,是否存在合法定向方案:
    • \(i\) 向右的边方向为 \(0/1\)。
    • 仅考虑 \(x_0 \sim x_i\)。
  • 转移分讨 \(f_{0/1,i-1} \to f_{0/1,i}\) 是否可行,只要根据 \(a_i,x_i\) 和 \(g_i\) 判定。
  • DP 初值为 \(f_{{\tt{R}},0}=1\)。
  • 答案为 \(f_{{\tt{L}},n}\)。

只要记录 \((x_{i-1},x_i,f_{0,i-1}, f_{1,i-1})\) 四元组,内层 DP 就能建模为 DFA,状态信息量 \(4^2 \times 2^2=64\)。考虑外层 DP:

  • 设 \(f_{u,v,f_0,f_1,i}\) 为满足下列条件的方案数:
    • \(x_{i-1}=u \land x_i=v\)。
    • \(f_{0/1,i-1}=f_{0/1}\)。
    • 仅考虑 \(x_0 \sim x_i\)。
  • 转移枚举 \(x_{i+1}\) 的取值,可算出新的 \((u',v',f_0',f_1')\),转移 \(f_{u,v,f_0,f_1,i-1} \to f_{u',v',f_0',f_1',i}\) 即可。
  • DP 初值为 \(f_{0,v,0,1,0}=1\)。
  • 答案为 \(\sum\limits_{u,f_1} f_{u,0,1,f_1,n}\)。

复杂度 \(O(k^3 n)\),其中 \(k=4\)。

const int N = 2e5 + 5;
int n, a[N], b[4][4][N]; 
mint f[4][4][2][2][N];

void _main() {
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i], a[i]--;
    for (int i = 1; i <= n; i++)
        for (int u = 0; u < 4; u++)
        for (int v = 0; v < 4; v++)
            cin >> b[u][v][i], b[u][v][i]--;
    for (int i = 0; i < 4; i++) f[0][i][0][1][0] = 1;
    for (int i = 1; i <= n; i++) 
        for (int u : {0, 1, 2, 3}) for (int v : {0, 1, 2, 3})
        for (int f0 : {0, 1}) for (int f1 : {0, 1}) 
        for (int w : {0, 1, 2, 3}) {
            if (i == n && w > 0) break;
            int nf0 = 0, nf1 = 0;
            if (f0) nf0 |= b[a[i-1]][w][i] == v, nf1 |= b[a[i-1]][a[i+1]][i] == v;
            if (f1) nf0 |= b[u][w][i] == v, nf1 |= b[u][a[i+1]][i] == v;
            f[v][w][nf0][nf1][i] += f[u][v][f0][f1][i - 1]; 
        }
    mint res = 0;
    for (int u = 0; u < 4; u++)
        for (int f1 : {0, 1}) res += f[u][0][1][f1][n];
    cout << res;
}
posted @ 2026-10-02 20:42  stripe_python  阅读(13)  评论(0)    收藏  举报