1. CF813D
题意:
给一个长度为(n)的序列,求两个不相交的子集长度之和最大是多少,能放入同一子集的条件是首先顺序不能变,然后每一个相邻的要么相差(1)或者相差(7)的倍数。 (n < 5000)
题解:
(f[i][j]) 表示第一序列到了第 (i) 位,第二个序列到了第 (j) 位,符合条件的长度之和最大.
那么显然可以退出 (O(n^3)) 方程:
i : 1 ~ n
j : i + 1 ~ n
k : 分两段。
第一段:1 ~ i,此时判一下a[k]有没有被j选中,如果没有在考虑转移i
第二段:j ~ n,此时就分别转移i,j就行了
那么这肯定是过不了的,所以还需要优化。
就是开一个桶记录相差 (1) 的 (max)
再开一个桶记录 (\%7) 同余
代码:
Rep(j, 1, i - 1) pre[a[j]] = max(pre[a[j]], f[i][j]), num[a[j] % 7] = max(num[a[j] % 7], f[i][j]);
Rep(j, i + 1, n) { f[i][j] = f[i][0] + 1;
f[i][j] = max(f[i][j], max(pre[a[j] + 1] + 1, max(pre[a[j] - 1] + 1, num[a[j] % 7] + 1))); f[j][i] = f[i][j];
pre[a[j]] = max(pre[a[j]], f[i][j]), num[a[j] % 7] = max(num[a[j] % 7], f[i][j]); ans = max(ans, f[i][j]);
2.CF796E
题意:
有 (n) 道题目,有两个人分别会做某些题目,有 (p) 次偷看机会,每次可以偷看某个人最多连续 (k) 道题目,求最多偷看几道题目。
题解:
这题细节比较多,思路比较板。
具体 令(dp[i][j][L][R]) 表示当前为第 (i) 题,已经偷看了 (j) 次,还能向左边的大神看 (L) 道题,向右边的大神看 (R) 道题。
然后就随便转移(雾
代码
int now = f[(i + 1) % 2][j][L][R];
f[i % 2][j][max(L - 1, 0)][max(R - 1, 0)] = max(f[i % 2][j][max(L - 1, 0)][max(R - 1, 0)], now);
if(L) f[i % 2][j][L - 1][max(R - 1, 0)] = max(f[i % 2][j][L - 1][max(R - 1, 0)], now + a[i]);
if(R) f[i % 2][j][max(L - 1, 0)][R - 1] = max(f[i % 2][j][max(L - 1, 0)][R - 1], now + b[i]);
if(L) f[i % 2][j + 1][L - 1][k - 1] = max(f[i % 2][j + 1][L - 1][k - 1], now + (a[i] | b[i]));
if(R) f[i % 2][j + 1][k - 1][R - 1] = max(f[i % 2][j + 1][k - 1][R - 1], now + (a[i] | b[i]));
if(L && R) f[i % 2][j][L - 1][R - 1] = max(f[i % 2][j][L - 1][R - 1], now + (a[i] | b[i]));
f[i % 2][j + 1][k - 1][max(R - 1, 0)] = max(f[i % 2][j + 1][k - 1][max(R - 1, 0)], now + a[i]);
f[i % 2][j + 1][max(L - 1, 0)][k - 1] = max(f[i % 2][j + 1][max(L - 1, 0)][k - 1], now + b[i]);
f[i % 2][j + 2][k - 1][k - 1] = max(f[i % 2][j + 2][L - 1][R - 1], now + (a[i] | b[i]));
3.CF855C
题意:
给你一个树,可以染 (m) 个颜色,定义一个特殊颜色 (k) ,要求保证整棵树上特殊颜色的个数不超过 (x) 个。同时,如果一个节点是特殊颜色,那么它的相邻节点的颜色编号必须全部小于 (k)。求方案数。
题解:
经典的树上 ( ext{DP}) + ( ext{DP}) 优化 ( ext{DP})。
考虑 (f_{i, j, 0/1/2}) 表示以 (i) 为根的子树,选了 (j) 个特殊颜色,其中节点 (i) 的颜色小于/等于/大于 (k) .
这个转移的话肯定是有子树转移而来,(2) 个子树很好处理,相乘即可。但是多个子树呢?
就不那么好处理了,复杂度会很高。
所以我们考虑再用一个新的 ( ext{DP}) 去更新这个 ( ext{DP}) 。
设 (g_{u, i, j, 0/1/2}) 表示 (u) 的子树中考虑前 (i) 个儿子,选了 (j) 个 (k),节点 (i) 的状态是 (0/1/2) 的方案数。
那么这个也很好转移,即:
然后赋回给
但是这样写还不行,(g) 数组的空间会炸,所以把第二维滚掉即可。
代码
for(int j = 0; j <= x; ++ j) {
for(int kk = 0; kk <= j; ++ kk) {
g[qwq][now][j][0] = (g[qwq][now][j][0] + (g[qwq][now ^ 1][j - kk][0] * (f[v][kk][0] + f[v][kk][1] + f[v][kk][2]) % XRZ)) % XRZ;
g[qwq][now][j][1] = (g[qwq][now][j][1] + (g[qwq][now ^ 1][j - kk][1] * f[v][kk][0] % XRZ)) % XRZ;
g[qwq][now][j][2] = (g[qwq][now][j][2] + (g[qwq][now ^ 1][j - kk][2] * (f[v][kk][0] + f[v][kk][2]) % XRZ)) % XRZ;
}
}