#2754. Count(count)
题目描述
`n+e`接收到了小叶子的信息,你的 $\text{hack}$ 失败了!
这条信息是这样的:
To `n+e`:
我的桌子忘记整理了,听说待会检查卫生,求帮忙。
叶子
小叶子的桌面上有 $n$ 本高度不相同的书,`n+e`现在需要把这些书按照一定的顺序摆放好。假设第 $i$ 本书的高度为 $h[i]$ ,`n+e`的摆放用一个 $1~n$ 的排列 $p_i$ 来表示。
定义一个摆放的混乱程度: $|h[p_2]-h[p_1]|+|h[p_3]-h[p_2]|+...+|h[p_n]-h[p_{n-1}]|$ ,即相邻两本书的高度差的绝对值之和。
已知合法的摆放要求其混乱程度不超过 $L$ 。
小叶子想要知道,`n+e`到底有多少种合法的摆放的方法呢?
作为将要参加 $\text{NOI}$ 的选手,你应该知道,小叶子只关心这个数对 $10^9+7$ 取模的结果。
题解
可以考虑从大到小放书,设 $f[i][0/1][0/1][k][s]$ 表示从大到小排序后的前 $i$ 本书,第 $1$ 个位置有没有放书,第 $n$ 个位置有没有放书,当前有 $k$ 个坑,当前侧边暴露出的周长和为 $s$ 的方案数。这里的坑指的是最左边的书和最右边的书中间的空缺部分,我们可以考虑先把这些坑看成一个单位的。
考虑插书的过程,如果 $k>0$ 的话:
1.我们可以补上某个坑,即转移到 $k-1$ ;
2.我们可以在某个坑的两侧插书,然后这些坑保留不变,即转移到 $k$ ;
3.我们可以在某个坑的中间插书,然后将这个坑分裂,变成两个一个单位的坑,即转移到 $k+1$ 。
如果第 $1/n$ 个位置没放书的话可以再按照上述分讨一下。
考虑到周长记不下状态,所以我们可以考虑将某次插入的书设成地平线,然后每次只要记录这个地平线上边的书侧面的周长即可。效率: $O(n^2L)$ 。
代码
#include <bits/stdc++.h> using namespace std; const int N=105,M=1005,P=1e9+7; int n,m,a[N],f[2][2][2][N][M],X,Y=1,s; int main(){ cin>>n>>m; for (int i=1;i<=n;i++) scanf("%d",&a[i]);sort(a+1,a+n+1); if (n==1) return puts("1"),0; f[0][0][0][0][0]=f[0][1][0][0][0]=f[0][0][1][0][0]=1; for (int i=n-1;i;i--){ memset(f[Y],0,sizeof f[Y]); for (int l=0;l<2;l++) for (int r=0;r<2;r++) for (int k=0;k<=n;k++) for (int v,x,u=0;u<=m;u++){ v=u+(a[i+1]-a[i])*(k+k+(!l)+(!r)); if (v>m) break;x=f[X][l][r][k][u]; if (k){ (f[Y][l][r][k][v]+=1ll*x*(k+k)%P)%=P; (f[Y][l][r][k-1][v]+=1ll*x*k%P)%=P; (f[Y][l][r][k+1][v]+=1ll*x*k%P)%=P; } if (!l){ for (int j=0;j<2;j++) (f[Y][j][r][k][v]+=x)%=P, (f[Y][j][r][k+1][v]+=x)%=P; } if (!r){ for (int j=0;j<2;j++) (f[Y][l][j][k][v]+=x)%=P, (f[Y][l][j][k+1][v]+=x)%=P; } } X^=1;Y^=1; } for (int i=0;i<=m;i++) (s+=f[X][1][1][0][i])%=P; cout<<s<<endl;return 0; }