P7213 最古の遺跡 3 题解-dp 好题
P7213 最古の遺跡 3
见到两次了,但是第二次忘了,遂随手题解一篇。
原,太原了!
但也忘了
题目描述
你有 \(2N\) 个柱子,每个柱子各有一个高度,且对于 $ 1 \le k \le N $,各有两根柱子的高度为 \(k\)。现在,发生了 \(N\) 次地震,每次地震会使一些石柱的高度 \(-1\),其他石柱高度不变。
- 石柱 \(i\) 地震时高度不变,当且仅当 \(h_i\ge 1\) 并且对于 \(j>i\) 都要有 \(h_i\not=h_j\)。
\(N\) 次地震后,恰好只剩下了 \(N\) 个石柱。仅存的 \(N\) 个石柱的位置分别是 \(A_1,A_2,\ldots,A_N\),问最初 \(2N\) 个石柱高度的修建方案数 \(\bmod~10^9+7\) 的值。
$ n\le 600$,时间 4s。
分析
注意到,可以考虑从后往前震柱子。如果后面有所有的 $ 1\sim h $ 柱子,那么前面的 $ <h $ 的柱子全部木大。以此 dp $ f_{i,j} $ 表示考虑到后 \(i\) 个,其中 $ 1 \sim j$ 都有。
定义前面有 \(c_0\) 个柱子被震没了(不含自身),有 \(c_1\) 个柱子还在(称之为标准柱),那么分类讨论转移:
- 如果 \(i\) 震没了,合计 \(2j\) 种可能,标准柱用了 \(j\),其他震没柱用了 \(c_0\),则 $ f_{i,j} \leftarrow (j-c_0)f_{i+1,j} $.
- 如果 \(i\) 还在,那么分讨其震完后是否为 $ j+1 $:
-
- 若不是,则不管他,$ f_{i,j} \leftarrow f_{i+1,j} $;
-
- 否则,考虑有了 \(j+1\),能否将之前的一些柱子连起来。枚举连了 \(k\) 个柱子(包括自己),接下来分析系数:首先,标准柱本身的高度可能为 $ k-1 $ 种可能;其次,从前面任选一些标准柱作为 $j+2\sim k $,系数为 $\binom{c_1-j}{k-1} $;以及,那些柱子之间的顺序,称之为 \(g_k\).
接下来分析 \(g\)。不难想到 \(g_i\) 的组合意义是“有 \(i\) 个柱子没有被震没,且最后分别为 $ 1 \sim i $ 的方案数”。直接枚举最前一个柱子的高度 \(j\),将整个高度序列分成 \(j-1\) 和 \(j-i\) 两半,自己有 \(j+1\) 种选法,最后式子为 $g_i=\sum_{j\le i} \binom{i-1}{j-1} \times g_{j-1} \times g_{i-j} \times (j+1) $
综上,做完啦!复杂度 $ O(n^3) $
代码
void mian()
{
read(n);m=n*2;
for(int i=1;i<=n;i++)
{
int x;read(x);
c[x]=1;
}
g[0]=1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=i;j++)
{
modadd(g[i],1ll*g[j-1]*g[i-j]%Mod*C(i-1,j-1)%Mod*(j+1)%Mod);
}
}
int c0=0,c1=0;
f[m+1][0]=1;
for(int i=m;i;i--)
{
if(c[i]==0)
{
for(int j=c0;j<=n;j++)
{
f[i][j]=1ll*f[i+1][j]*(j-c0)%Mod;
}
c0++;
continue;
}
for(int j=0;j<=c1+1;j++)
{
f[i][j]=f[i+1][j];
for(int k=0;k<j;k++)
{
modadd(f[i][j],1ll*f[i+1][k]*C(c1-k,j-k-1)%Mod*(j-k+1)%Mod*g[j-k-1]%Mod);
}
}
c1++;
}
int res=f[1][n];
modmul(res,ksm(inv(2),n));
cout<<res;
}

浙公网安备 33010602011771号