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;
}

浙公网安备 33010602011771号