我似乎记得 Polya 的《怎样解题表》提示我 “从尽可能多不同的角度将原问题进行描述” ,也似乎记得有某篇集训队论文的题目叫做《退一步,海阔天空》 ,我想,用一个流程来对题目中的合法状态进行描述,能获得许多不一样的理解吧。
就目前做的题来看,这种观点大多用于计数问题,或者一些最优性问题。
[TH 2140] 计数
给 $n_1(\le 1000)$ 个 $A$ 、$n_2(\le 1000)$ 个 $B$ 、$n_3(\le 1000)$ 个 $C$ 、$n_4(\le 1000)$ 个 $D$ ,求能组成多少个相邻项不同的序列。
用一个流程描述所有序列,然后对流程的不同运行数进行计数:对 $A$ 与 $B$ 归并,对 $C$ 与 $D$ 归并,然后将两个序列相互渗入。
两个序列不一定能够相互渗入。对由两种字母构成的序列定义渗入指数 $K$ :强制把序列钦定为 $K$ 段,相互渗入的时候在两段之间的空隙必须插入。那么两个序列能够相互渗入,当且仅当渗入系数的差值的绝对值不超过 $1$ 。
如何求由 $n_1$ 个 $A$ ,$n_2$ 个 $B$ ,构造的渗入系数为 $K$ 的序列个数?
观察性质发现,对于每一段,只可能有三种情况:
(1)$A$ 比 $B$ 多 $1$ 个;
(2)$B$ 比 $A$ 多 $1$ 个;
(3)$A$ 和 $B$ 一样多。
枚举 $A$ 比 $B$ 多 $1$ 个的段数 $a$ ,可以解决 $B$ 比 $A$ 多 $1$ 个的段数 $b$ ,以及 $A$ 、$B$ 一样多的对数 $c$ 。
通过 $\binom{K}{a, b}$ 钦定哪些段是情况(1)(2),然后乘上 $2 ^ {K - a - b}$ 钦定 $A$ 、$B$ 一样多的段数是形如 $ABAB$ 还是 $BABA$ 。剩下的问题就是将 $c - (K - a - b)$ 对 $ab$ 划分到 $K$ 段中,这是经典的不定方程计数,利用插板法。
1 const int N = 2005; 2 const int MOD = 1e9 + 7; 3 4 int C[N][N], Pow[N]; 5 int n1, n2, n3, n4; 6 int f[N], g[N], ans; 7 8 inline int Solu(int n, int m) { 9 // a[1] + a[2] + ... + a[n] = m, a[i] >= 0 10 if (n == 0) return m == 0; 11 return C[m+n-1][n-1]; 12 } 13 void Init(int nA, int nB, int *h) { 14 F(K, 0, nA+nB) { 15 F(a, 0, K) { 16 int b = nA - nB + a; 17 int c = K - a - b; 18 int d = (nA + nB - a - b) >> 1; 19 if (!(a >= 0 && b >= 0 && c >= 0 && d >= c)) continue; 20 h[K] = (h[K] + 1LL * C[K][a] * C[K-a][b] % MOD * Solu(K, d-c) % MOD * Pow[c]) % MOD; 21 } 22 } 23 } 24 25 int main(void) { 26 For(i, 0, N) { 27 C[i][0] = 1; 28 F(j, 1, i) C[i][j] = (C[i-1][j] + C[i-1][j-1]) % MOD; 29 } 30 Pow[0] = 1; 31 For(i, 1, N) Pow[i] = (Pow[i-1] << 1) % MOD; 32 33 scanf("%d %d %d %d", &n1, &n2, &n3, &n4); 34 Init(n1, n2, f); 35 Init(n3, n4, g); 36 37 F(i, 1, n1+n2) 38 ans = (ans + 1LL * f[i] * g[i-1] + 2LL * f[i] * g[i] + 1LL * f[i] * g[i+1]) % MOD; 39 printf("%d\n", ans); 40 }
[HDU 6017] Girl Love 233
给长度为 $n(\le 100)$ 的由 $2$ 、$3$ 组成的序列,求对序列进行不超过 $m(m \le 100)$ 次交换相邻两位的所有新序列中,最多的连续的 $233$ 的个数。
所有新序列可以看作:由 $2$ 、$3$ 归并而成,且最少的交换次数小于等于 $m$ 。即设原序列 $2$ 的位置为 $a_1, a_2, ..., a_k$ ,新序列 $2$ 的位置为 $b_1, b_2, ..., b_k$ ,则要求 $\sum |a_i - b_i| \le m$ 。
设 $f[i][j][k][l]$ 表示在归并的过程中,用了 $i$ 个 $2$ 、$j$ 个 $3$ 、$k$ 次交换、末尾状态为 $l$ 的最多的连续的 $233$ 个数,DP 即可。
[BZOJ 3864] Hero meet devil
字符集为 $\left\{ ‘A‘, ‘G‘, ‘C‘, ‘T‘ \right\}$ ,给长度为 $n(\le 15)$ 的字符串 $S$ ,求有多少个长度为 $m(\le 1000)$ 的字符串 $T$ ,使得 $LCS(S, T) = 0, 1, ..., n$ 。
如何求 $LCS(S, T)$ ?设 $f[i][j]$ 表示 $LCS(T[1:i], S[1:j])$ ,由 $f[i-1][]$ 推出 $f[i][]$ 。换句话说,假如把每一行当作一种变量类型命名为 $T$ 的状态,那么我由 $f[0]$ 推得 $f[1]$ ,$f[1]$ 推得 $f[2]$ ,依次类推。综上,用了一个流程来描述 $LCS(S, T)$ 。
对这个流程套用 DP ,设 $g[i][State]$ 表示考虑到第 $i$ 位的状态,且 $f[i] = State$ 的方案数。
在 $State$ 中,相邻两位的差值为 $0$ 或 $1$(可以通过数学归纳法进行证明),且第一位的值为 $0$ ,所以可以通过二进制来表示所有的状态,且状态数为 $O(2 ^ n)$ 。
[CF 578D] LCS Again
字符集 $\alpha = \left\{ 前 m 个小写字母 \right\}$ ,给长度为 $n(\le 100000)$ 的串 $S$ ,求有多少个串 $T$ ,使得 $LCS(S, T) = n-1$ 。
对于给定的 $S, T$ ,如何判断 $S, T$ 的 $LCS$ 是否为 $n-1$ ?假设 $S, T$ 的 $LCS$ 为 $n-1$ ,设 $f[i][j]$ 表示 $T[1 : i]$ 与 $S[1 : j]$ 的 $LCS$ ,$f[i][j] = \min(f[i-1][j], f[i][j-1], f[i-1][j-1] + [ T_i = S_j ])$ ,但是这样的判定是 $O(n ^ 2)$ 的,实在不可取。
将 $f[][]$ 理解为二维平面上的 $n \times n$ 个点,定义横向边、纵向边的边权为 $0$ ,对角边的边权为 $1$ ,问从 $(0, 0)$ 走到 $(n, n)$ 的最大边权之和是否为 $n-1$ 。假设一条从 $(0, 0)$ 走到 $(n, n)$ 的路径经过了 $(i, j)$ ,那么边权之和的最大值为 $Max = \min(i, j) + \min(n - i, n - j) = n - \max(i, j) + \min(i, j)$ ,那么要求 $Max \ge n-1$ ,所以 $|i - j| \le 1$ 。所以,对于 $|i - j| > 1$ ,对答案没有贡献,只需要维护 $f[i][i-1], f[i][i], f[i][i+1]$ 即可。且任何时候如果 $f[i][i]$ 不等于 $i$ 或 $i-1$ ,那么 $f[i][i] < i-1$ ,对答案也没有贡献,直接宣告判断失败。
把一行看作一个整体的状态,我由 $f[0]$ 推出 $f[1]$ ,$f[1]$ 推出 $f[2]$ ,依次类推。设 $g[i][S]$ 表示推到 $f[i]$ ,且 $f[i] = S$ 的方案数,对 $g$ 进行 DP 。由于 $S$ 满足 $f[i][i] = i$ 或 $f[i][i] = i-1$ ,且 $f[i][i] - f[i][i-1] \le 1$ ,$f[i][i+1] - f[i][i] \le 1$ ,所以可以使用 $2 ^ 3$ 个二进制状态进行表示,总的时间复杂度为 $O(2 ^ 3 nm)$ 。
[TH 1587] 小方格
给 $n \times n(n \le 8)$ 的方格,有坏点。对剩下的格子进行黑白染色,问最大纯白色正方形边长为 $0, 1, ..., n$ 的方案数。
差分,转化为求最大纯白色正方形边长小于等于 $x$ 的方案数 $ans[]$ 。
对于给定的染色方案,如何判定最大纯白色正方形的面积是否小于等于 $x$ ?逐行扫描,维护 $S = \left\{ s_1, s_2, ..., s_{n - x} \right\}$ ,$s_i$ 表示扫描到当前行时,第 $i, i+1, i+2, ..., i+x$ 列共同隆起的长度,当 $s_i = x+1$ 时,判定失败。
设 $g[i][T]$ 表示扫描到第 $i$ 行,当前的 $S = T$ 的方案数。$S$ 用长度为 $n-x$ 的 $x+1$ 进制数进行表示。
[TH 1305] 填格子
给 $2 \times m(m \le 1000000)$ 的格子,填 $A$ 、$B$ 、$C$ ,满足:
(1)田字格中,每种颜色都出现过;
(2)相邻颜色不同;
(3)$A$ 有 $a$ 个、$B$ 有 $b$ 个、$C$ 有 $c$ 个,保证 $r + g + b = 2m$ 。
把满足(1)(2)的状态称为合法状态。确定相邻互不相同的第一行,再确定第一行与第二行的对应关系,可以不重不漏地描述出所有的合法状态。
第一行与第二行的对应关系有两种:
第一种 |
第二种 | ||||
A | B | C | A | B | C |
| | | | | | | | | | | |
C | A | B | B | C | A |
对满足第一种对应关系、满足(3)的合法状态个数进行计数:设有 $x$ 个 $A$ 对应 $C$ ,$y$ 个 $B$ 对应 $A$ ,$z$ 个 $C$ 对应 $B$ ,合法当且仅当 $x + y = a, y + z = b, z + x = c$ ,可以唯一地解得 $(x, y, z) = (m - a, m - b, m - c)$ ,所以只需要求将 $x$ 个 $A$ 、$y$ 个 $B$ 、$z$ 个 $C$ 排成一排,且相邻两个不同的方案数,可以用 [TH 2140] 计数 一题的方法做。
对满足第二种对应关系,$(x, y, z)$ 的解相同,把第一次的答案乘 $2$ 即可。
[TH 1841] Intervals
在长度为 $m$ 的序列选 $n$ 个互不包含的区间,且点 $x$ 不能作为区间的左端点,求方案数。
$n \times m \le 100000$ 。
以每个点作为左端点(右端点),最多只有一个区间,否则包含。所以若合法,那么 $n \le m$ ,所以 $n \le 500$ 。
用流程描述合法选择方案:从左往右扫描,时刻维护左端点队列 $Q$ ;扫到每个点的时候,可以在这个点建立一个左端点,将左端点加入 $Q$ ,可以在这个点建立一个右端点,将 $Q$ 的队头与当前点匹配;扫描完之后 $Q$ 空。
对这个流程进行 DP ,$f[i][j][k]$ 表示当前扫描到了第 $i$ 个点,已经确定了 $j$ 个区间的左端点,当前队列中有 $k$ 个区间的左端点的方案数。边界为 $f[0][0][0] = 1$ ,答案为 $f[m][n][0]$ ,转移分 "是否建立左端点" 、"是否建立右端点" 共四类讨论。
[AGC 002F] 球
有 $n(\le 2000)$ 种颜色的球,每种各有 $K(\le 2000)$ 个,将这 $n \times K$ 个球排成一列,将每种球的第一个染成颜色 $0$ ,问可能得到多少种不同的颜色序列。
钦定第一次出现的球的顺序:第 $1$ 个第一次出现的球的颜色为 $1$ 、第 $2$ 个第一次出现的球的颜色为 $2$ 、...、第 $n$ 个第一次出现的球的颜色为 $n$ ,然后对答案乘上 $n!$ 。
可以通过这样一个流程来描述最终的染色序列:从 $n$ 到 $1$ 枚举颜色 $i$ ,每次将 $K-1$ 个颜色 $i$ 的球和 $1$ 个颜色为 $0$ 的球插入序列中,保证 $0$ 插在序列的开头,第一个 $i$ 要在第一个 $i+1$ 之前。
受此启发,直接 DP :设 $f[i][j]$ 表示考虑了颜色 $[n-i+1, n]$ ,序列的开头有 $j$ 个 $0$ 的方案数。