总结 9.7
有两道 qoj 的还没看。
AGC022E Median Replace
题意:一个长度为 \(n\) 的 \(01\) 串,有一些位置为通配符。若存在一种方案,每次消去三个连续的字符,然后在相同位置插入它们的中位数,共操作 \(\frac {n-1} 2\) 次后,剩下的数是 \(1\),那么这是一个完美的 \(01\) 串(保证 \(n\) 是奇数)。求有多少个完美的 \(01\) 串,模 \(10^9+7\)。\(1\le n\le3\times10^5\)
手玩一下,不难发现几个点:
-
消 \(000\) 肯定最赚。
-
存在 \(0\) 时,不可能消 \(111\)。
-
消除有 \(0\) 有 \(1\) 的三个字符,相当于删掉一组 \(01\)。
进一步贪心模拟,设当前我们保留了 \(x\) 个 \(0\) 和 \(y\) 个 \(1\)。
-
遇到一个 \(0\):令 \(y\leftarrow y+1\),当 \(y=3\) 时,令 \(y\) 变为 \(1\)。
-
遇到一个 \(1\):如果 \(y>0\),中间的 \(y\) 个 \(0\) 肯定要消去,不妨直接令这个 \(1\) 与其中一个 \(0\) 一起删掉,即 \(y\leftarrow y-1\);否则,\(x\leftarrow x+1\)。
最终合法的条件即为 \(x\ge y\)。
不难发现,\(y\) 始终满足 \(y\le 2\)。
于是令 \(x'=\min(x,2)\),设 \(f[i,j,k]\) 表示考虑到第 \(i\) 位,此时 \(x'=j,k=y\) 时的方案数。
答案:\(f[n,0,0]+f[n,1,0]+f[n,2,0]+f[n,1,1]+f[n,2,1]+f[n,2,2]\)。
UOJ748 【UNR #6】机器人表演
题意:有一个长为 \(n\) 的 \(01\) 串,你需要计算 \(t\) 次操作后能得到多少不同的 \(01\) 串。一次操作的定义为:在串中选两个位置插入一对 \(01\) 使得 \(0\) 在 \(1\) 前。\(1\le n,t\le300\)
不难得到一种 \(\text{DP}\):设 \(f[i,j,k]\) 表示考虑了最终串的前 \(i\) 位,与给出的串匹配了 \(j\) 位,剩下位置的字符的权值和(字符 \(0\) 的权值为 \(1\),字符 \(1\) 的权值为 \(-1\))为 \(k\) 的方案数。
转移:
-
加入一个 \(0\):如果与给出串的 \(j+1\) 位相同,那么 \(f[i+1,j+1,k]\leftarrow f[i+1,j+1,k]+f[i,j,k]\);否则 \(f[i+1,j,k+1]\leftarrow f[i+1,j,k+1]+f[i,j,k]\)
-
加入一个 \(1\):如果与给出串的 \(j+1\) 位相同,那么 \(f[i+1,j+1,k]\leftarrow f[i+1,j+1,k]+f[i,j,k]\);否则 \(f[i+1,j,k-1]\leftarrow f[i+1,j,k-1]+f[i,j,k](k>0)\)
我们在此过程中的“匹配”是贪心匹配,不一定考虑到了所有的方案。比如给出的串为 \(000\),操作 \(1\) 次后有合法的最终串 \(00100\),但我们可能会先把前两位贪心匹配了,此时 \(j=2,k=0\),我们在 \(\text{DP}\) 中就没有计算到第 \(3\) 位填 \(1\) 的情况(因为此时 \(k=0\))。
正确的做法应该是在原来贪心地匹配上加点料,如果当前 \(k=0\),我们考虑撤回一些匹配位,用这些位置凑出权值和为 \(1\) 让给 \(k\)。设 \(to[j]\) 表示撤销到哪个位置后可以凑出权值和为 \(1\),那么 \(f[i,j,0]\rightarrow f[i+1,to[j],0]\)。
答案:\(f[n+2t,n,0]\)
CF gym 103860I
题意:给出一个 \(01\) 序列,你可以把序列的某一段翻转,可以翻转 \(k\) 次,求最终序列的 \(\text{LIS}\) 最大是多少。
考虑如果没有翻转操作,枚举一个位置 \(i\),设 \(s_0(x)\) 表示 \(1...x\) 中 \(0\) 的个数,\(s_1(x)\) 表示 \(x+1...n\) 中 \(1\) 的个数,那么答案为 \(\max\limits_i\{s_0(i)+s_1(i+1)\}\)
有两个量看着不顺眼,设 \(s(x)\) 表示 \(0\) 的权值为 \(1\),\(1\) 的权值为 \(-1\) 时 \(1...x\) 的权值和,那么答案为 \(s_1(n)+\max\limits_i\{s(i)\}\)
考虑翻转怎么做。手玩一下,发现其实就是选择一个前缀,再选 \(k\) 个互不相交且不与前缀相交的段,每一段的权值和总和的最大值。
考虑先给第一个位置的权值加个 \(\inf\),问题变成了选择 \(k+1\) 个互不相交的段。
这是一个经典问题,用线段树维护模拟费用流即可。
CF1416E Split
题意:给出 \(n,a_{1...n}\),把每个 \(a_i\) 分裂成两个正整数,他们的和为 \(a_i\)。分裂后形成了一个有 \(2n\) 个数的序列,求这个序列的相同连续段数的最小值。\(1\le n\le5\times 10^5\)
考虑 \(\text{DP}\)。
“相同连续段数的最小值”相当于求“相邻相同数字对数的最大值”,设 \(f[i,j]\) 表示考虑了 \(a_{1...i}\) 的分裂,\(a_i\) 分裂出来的后一个数为 \(j\) 时的相邻相同数字对数的最大值。
这个 \(\text{DP}\) 非常特殊,单个 \(a_i\) 的贡献最大为 \(2\),而且单个 \(f[i-1,k]\) 对所有的 \(f[i,j]\) 都有贡献。
设 \(g=\max_k\{f[i-1,k]\}\),那么
后面那个 \([2j=a_i]\) 属于最后的单点修改,我们可以先不管。考虑前面的,发现当 \(f[i,a_i-j]=g\) 时 \(f[i,j]\) 才等于 \(g+1\),否则等于 \(g\)。
这启示我们存下一个最优决策集合。设 \(S\) 表示 \(k\in S,f[i-1,k]=g\)。转移时先把 \(S\) 中 \(\ge a_i\) 的数删掉,然后把所有 \(x\in S\) 变成 \(a_i-x\)。如果 \(a_i\) 为偶数,并且 \(\frac {a_i} 2\in S\) 时,此时 \(f[i,\frac{a_i}2]\) 就会变成最大的数,令 \(S\) 中只剩一个数 \(\frac{a_i}2\),然后更新 \(g\);若 \(\frac {a_i}2\not \in S\),则把 \(\frac{a_i}2\) 加入 \(S\)。
不难发现,\(S\) 中的数一定是形如“一个区间 + 一些单点”,单点数量是 \(O(n)\) 的,容易维护。对于把 \(S\) 中的操作 \(x\in S,x\leftarrow a_i-x\),我们维护一个乘法标记 \(mul\) 和一个加法标记 \(add\),令 \(add\leftarrow add\times (-1),mul\leftarrow mul\times(-1),add\leftarrow add+a_i\) 即可。集合可以用 \(\text{set}\)。
时间复杂度 \(O(n\log n)\)。
CF1500F Cupboards Jumps
题意:给出 \(n\) 和 \(w_{1...n-2}\),你需要构造 \(a_{1...n}\),满足 \(\forall i(1\le i\le n-2),w_i=\max(a_i,a_{i+1},a_{i+2})-\min(a_i,a_{i+1},a_{i+2})\),给出构造方案,或确定无解。\(3\le n\le 10^6\)
有个重要的套路:设 \(b_i=a_{i+1}-a_i(1\le i\le n-1)\),那么 \(w_i=max(|b_i|,|b_{i+1}|,|b_i+b_{i+1}|)\)。进一步的,若 \(b_i,b_{i+1}\) 同号,\(w_i=|b_i+b_{i+1}|=|b_i|+|b_{i+1}|\),否则 \(w_i=max(|b_i|,|b_{i+1}|)\)。设 \(c_i=|b_i|\),那么 \(w_i\) 等于 \(c_i+c_{i+1}\) 或 \(\max(c_i,c_{i+1})\)。我们只需要构造出 \(c_{1...n-1}\),然后推出 \(b_{1...n-1}\) 和 \(a_{1...n}\)。
不难想到 \(\text{DP}\),设 \(f[i,j]=0/1\) 表示构造了 \(a_{1...i}\),并且 \(a_i=j\),是否存在这样的方案。
考虑转移:
-
\(f[i,j]\rightarrow f[i+1,w_i]\)
-
\(f[i,w_i]\rightarrow f[i+1,0...w_i]\)
-
\(f[i,j]\rightarrow f[i+1,w_i-j]\)
用人话说:
-
先排除掉 \(f[i,w_i+1...\inf]\)
-
如果 \(f[i,0...w_i]\) 中有 \(1\),那么 \(f[i+1,w_i]=1\)。
-
如果 \(f[i,w_i]=1\),那么 \(f[i+1,0...w_i]\) 全为 \(1\)。
-
对于一个位置 \(j\),如果 \(f[i,j]=1\),那么令 \(f[i+1,w_i-j]=1\)。
把 \(i\) 看做常量,考虑维护一个集合 \(S\),表示 \(j\in S,\space f[i,j]=1\)。在 \(i\rightarrow i+1\) 的过程中,\(S\) 有如下变动:
-
把 \(S\) 中 \(>a_i\) 的数删掉。如果删完后集合为空,那么无解。
-
如果 \(a_i\) 在 \(S\) 中,把 \(0...a_i-1\) 全部加入集合 \(S\);否则先把集合中所有的 \(j\) 变成 \(a_i-j\),然后把 \(a_i\) 加入 \(S\)。
不难发现,\(S\) 中的数总是“一个区间+一些单点”,单点数量是 \(O(n)\) 的,维护方法和上题类似。特别的,每次加入 \(a_i\) 总是 \(S\) 中最大的,可以用 \(\text{deque}\) 来代替 \(\text{set}\)。
然后构造方案直接反推,时间复杂度 \(O(n)\)。