在 CF 本场比赛的讨论区发现了解决这道题的一个比较新颖的思路,并且可以以 \(O(n^2)\) 的时间解决本题,来分享一下。
姑且称这种 DP 方式为组件 DP。
在普通的 DP 方式中,我们常常针对位置进行 DP ,在组件 DP 中,我们按操作次序针对每个操作进行 DP。
为了描述方便,记我们手动做出的选择为 ? ,记自动被选择为 ? 。
如果一些操作对应的位置连通,我们称这些操作构成一组组件。例如,我们第一次操作手动选择一号位置,第二次操作手动选择二号位置,这两个操作构成了一组组件( ?? )。
而对于每次可以进行的操作,我们称其为一个元件。本题中元件为: ? ,表示手动选择了一个位置。
设 \(f[i][j]\) 表示前 \(i\) 次操作后,存在 \(j\) 个组件的方案数。
我们有三种转移方式:
若当前有 \(j\) 组组件,则一共会存在 $ j+1$ 段间隔,在其中任意一段间隔中都可以以插入一个新元件 ? 的方式添加一个组件。
除了添加一个组件外,我们还可以在保证原有组件都联通的基础上增加一个原有组件的长度。在本题中有两种添加方式:在一个组件紧挨着的位置上插入一个新元件 ? ;或者在一个组件留一个空位,在这个空位旁插入一个新元件 ? ,随后这个空位会自动被 ? 填充。
由于最后所有的操作必然要全部联通,因此我们还需要考虑如何将两个组件合并。
如果两个组件间有两个空位,我们选择任意一个空位插入一个新元件 ? ,随后另一个空位会自动被 ? 填充。
如果两个组件间有三个空位,我们选择中间的空位插入一个新元件 ? ,随后其余两个空位会自动被 ? 填充。
最后答案即为答案即为 \(f[n][1]\) ,时间复杂度 \(O(n^2)\)。
f[1][1]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
add(f[i+1][j+1],f[i][j]*(j+1));
add(f[i+1][j],f[i][j]*2*j);
add(f[i+2][j],f[i][j]*2*j);
if(j>1)add(f[i+2][j-1],f[i][j]*2*(j-1)),
add(f[i+3][j-1],f[i][j]*(j-1));
}
cout<<f[n][1]<<endl;
组件DP & 题解 CF1515E Phoenix and Computers
原文:https://www.cnblogs.com/Robert-JYH/p/14727596.html