Codeforces 1178F1

原题链接

题意

给定1个长度为\(n\)的排列。

初始时你有1个全部为0的数组,接下来你会进行\(n\)轮染色操作,第\(i\)轮染色操作会选择一段连续的颜色相同的格子并将其染成颜色\(i\)。询问有多少种方案能够染出目标排列。

对于60%的数据,\(1 \leq n \leq 80\)

对于100%的数据,\(1 \leq n \leq 500\)

题解

重大反思:比赛的时候,认为后面的操作会覆盖前面的,且又要保证得到目标序列,因此从逆向考虑。但这样做其实问题很严重,逆向操作时,你要保证轮到\(i\)时,\(i\)只能是连续的一段。光是这一点,就使得我们没办法用合适的状态来承载。只剩了暴搜一条路。

不要思维固化!不要思维固化!后面覆盖前面也不一定要从目标开始进行啊,限制住操作的覆盖范围同样能够求解。多方尝试才是硬道理。

考虑从前往后依次染色的过程,第\(i\)轮时染色的\([l_i,r_i]\)只需保证是相同色块且包含目标排列中的位置\(pos_i\)“保证是相同色块”,除了被动选择,也可以是主动限制子问题。具体地,假设第\(i\)轮染色完成,区间为\([l_i,r_i]\),则\(i\)轮之后的染色不能跨越\(pos_i\),也不能跨越当前颜色不同的区域s,只有4种可能,在\([l_i,pos_i - 1]\)/\([pos_i + 1,r_i]\)/\([1,l_i - 1]\)/\([r_i + 1,n]\)中选择区间染色,子问题一目了然。设计\(f[l,r]\)表示将区间\([l,r]\)全部染色成功的方案数,枚举其中最小的元素的左右边界进行转移,时间复杂度\(O(n ^ 4)\)

本质上我们只有\(n ^2\)个区间,考虑更快速地转移。

可以考虑1下更深层的本质,也可以将原始转移式写出后,结合其特征再考虑进一步的优化。\(f[i,j] = \sum_{l = i}^{mid}\sum_{r = mid}^{j}f[i,l - 1] \times f[l,mid - 1] \times f[mid + 1,r]\times f[r + 1,j]\)

可以发现左右两边的子问题互相独立。对于不同的\(l\)而言,\(r\)的贡献和是相同的,故可以直接对\(r\)的贡献做1次预处理。具体地,设\(g[i,j] = \sum_{k = i}^{j}f[i][k] \times f[k + 1][j]\),则\(f[i,j] = \sum_{l = i}^{mid - 1}f[i][l - 1] \times f[l,mid - 1] \times g[mid + 1,j]\)

时间复杂度\(O(n ^ 3)\)。[代码见此](https://github.com/littlewyy/OI/blob/master/cf 1178F1.cpp)

posted @ 2019-10-24 10:54  littlewyy  阅读(139)  评论(0编辑  收藏  举报