[DP记录]ARC104F Visibility Sequence
command_block
2021-10-26 07:33:47
**题意** : 对于的序列 $H_{1\sim n}$ ,定义其“前邻序列”为 $P_{1\sim n}$ ,其中 $P_i=\max\{{j:j<i,H_j>H_i}\}$ ,特殊地,若不存在 $j$ 满足条件,则 $P_i=-1$。
现在给定序列 $X_{1\sim n}$ ,请问对于所有满足 $1≤H_i≤X_i$ 的序列 $H_{1
\sim n}$ ,有多少个不同的“前邻序列”。
答案对 $10^9+7$ 取模。
$n\le 100,x\leq 10^5$ ,时限$\texttt{2s}$。
------------
经典的给个映射求投影大小。具体流程有:
- 先观察 $H\rightarrow P$ ,找出 $P$ 的某些必要性质
- 证明这性质是充分的(不对 $H$ 设限,看是否对于每个满足性质的 $P$ 都存在 $H_*\rightarrow P$)
- 找出满足 $H\rightarrow P$ 的 $H$ 的充要条件 (注意,上一步相当于构造特例,这一步是描述解集)
- 考虑 $X$ 的限制,构造判定 “是否存在 $H$ 使得 $H\rightarrow P$ ” 的算法。
- 根据上述判定算法设计 DP ,进行计数。
------------
$P$ 是一种下标关系,可以写成图的形式,便于观察。对于每个 $i$ 连边 $i\rightarrow P_i$ 。若 $P_i=-1$ 则不连边。
除了 $P_i=-1$ 的情况,每个点都恰有一条向前连的出边,图为**内向树森林**。
可以发现, $1\sim n$ 显然是这森林的 dfs 序之一。
观察片刻,这条性质应当很充分了。考虑不对 $H$ 加以限制,是否能构造出所有满足上述性质的树。
答案是肯定的。对于一片满足性质的森林,把标号从前往后从小往大划分,转化为满足性质的树,则这些树之间不相干了。对于每棵树,给根填上目前值域的最大值,然后又转化为森林。
不难进一步总结出 $H\rightarrow P$ 的 $H$ 的充要条件: 对于节点 $u$ ,必须有 $H_u>\max\limits_{v\text{是}u\text{儿子}}H_v$ ,且 $H_u\geq $ 上一个兄弟 $H_{u'}$。
------------
接下来考虑如何判定某个 $P$ 能否被生成。简单起见,我们希望为 $P$ 构造一个对应的“最优”的 $H_0$ ,使得若 $H_0$ 不满足条件,其余方案也不满足。(这样在判定时,和 $X$ 的具体取值关系很简单,这是一个好兆头)
观察 $X$ 对 $H$ 的限制,每个位置都有一个上界。我们要让 $H_0$ 的每一位同时达到下界。这并不一定总能实现,但我们不妨试试,毕竟那些不能实现的情况往往太过复杂,不值得优先考虑。
观察前文的充要条件,一个 $H$ 的约束基于其余若干 $H$ 的 $\max$ 。那当然是他们都取得下界的时候,该 $H$ 也能取得下界,于是不难归纳证明各个位置的下界能同时取得,好耶!
具体方案也非常简单,只需要根据充要条件,按 dfs 序考虑,每次取能取的最小值即可。
------------
接下来考虑如何计数。
由于 $1\sim n$ 是这森林的 dfs 序,每个子树的 dfs 序必然是一个编号区间。
设 $f_{l,r,x}$ 为 $[l,r]$ 形成一棵树的情况,且 $H_l=x$ (子树的根)。
$g_{l,r,x}$ 为 $[l,r]$ 形成一片森林的情况,其中最大权值(最后一棵树的根)为 $x$ 。
这里显然有 $x\leq r-l+1$ 。
边界:$f_{i,i,1}=g_{i,i,1}=1$ 。
对于 $f$ ,只需在 $g$ 的基础上加一个根,则 $f_{l-1,r,x+1}=[X_{l-1}\geq x+1]g_{l,r,x}$ 。
对于 $g$ ,枚举**最后一棵树** $[k+1,r]$ ,这样最大值的位置就确定为 $k+1$,有转移 :
$$g_{l,r,x}=f_{l,r,x}+\sum\limits_{k=l,\ X_{k+1}\geq x}^{r-1}\Bigg(f_{l,r,x}\sum\limits_{c=l}^xg_{k+1,r,c}\Bigg)+\Bigg(g_{k+1,r,x}\sum\limits_{c=l}^{x-1}f_{l,k,c}\Bigg)$$
(其实是当 $f_{l,k,x_1},g_{k+1,r,x_2}$ 的 $\max(x_1,x_2)=x\leq X_{k+1}$ 时能转移)
对 $c$ 前缀和优化,记 $Sf_{l,r,x}=\sum\limits_{c=l}^xf_{l,k,c},Sg_{l,r,x}=\sum\limits_{c=l}^xg_{l,k,c}$ ,然后暴力求和。
复杂度 $O(n^4)$。
```cpp
#include<algorithm>
#include<cstdio>
#define MaxN 105
using namespace std;
const int mod=1000000007;
int g[MaxN][MaxN][MaxN],f[MaxN][MaxN][MaxN]
,sg[MaxN][MaxN][MaxN],sf[MaxN][MaxN][MaxN];
int n,X[MaxN];
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)scanf("%d",&X[i]);
for (int i=1;i<=n;i++)
f[i][i][1]=g[i][i][1]=sf[i][i][1]=sg[i][i][1]=1;
for (int len=2;len<=n;len++)
for (int l=1;l+len-1<=n;l++){
int r=l+len-1;
for (int x=1;x<=r-l+1;x++){
if (X[l]>=x)f[l][r][x]=g[l+1][r][x-1];
g[l][r][x]=f[l][r][x];
for (int k=l;k<r;k++)if (X[k+1]>=x)
g[l][r][x]=(g[l][r][x]
+1ll*g[l][k][x]*sf[k+1][r][min(x,r-k)]
+1ll*f[k+1][r][x]*sg[l][k][min(x-1,k-l+1)]
)%mod;
}
for (int x=1;x<=r-l+1;x++){
sf[l][r][x]=(sf[l][r][x-1]+f[l][r][x])%mod;
sg[l][r][x]=(sg[l][r][x-1]+g[l][r][x])%mod;
}
}
int ans=0;
for (int x=1;x<=n;x++)ans=(ans+g[1][n][x])%mod;
printf("%d",ans);
return 0;
}
```