Count
题解
简单dp。
首先发现这道题的贡献是绝对值,有点烦,考虑如何去掉绝对值。
我们可以对
h
h
h从大到小排序,这样加入
h
i
h_{i}
hi时,已经加入的点会对其产生贡献的一定都比它大,所以我们可以直接加。
考虑每个点加入时会产生怎样的贡献:
- 如果加入的点单独成段且不靠墙,会令贡献加上 2 h i 2h_{i} 2hi。
- 如果加入的点在某一段的两侧或靠一堵墙将不产生贡献。
- 如果加入的点两侧全是墙与段,它将起连接两段的作用,会令贡献减去 ( 2 / 1 ) h i (2/1)h_{i} (2/1)hi。
于是我们很快就想到了一个dp方法,令
d
p
i
,
j
,
t
,
k
dp_{i,j,t,k}
dpi,j,t,k表示到第
i
i
i大的书,已经成了
j
j
j段,总贡献为
t
t
t,靠
k
k
k堵墙的方案数。
状态转移方程式也很好想。
但这样有个问题,因为贡献这一维带减法,所以它有可能有部分大于
L
L
L很多,这样空间复杂度就超了。
考虑对贡献的求法进行一下转化。
我们发现加入一个点时会对贡献产生怎样的影响。
假设有
j
j
j段,
k
k
k堵墙,贡献会受到怎样的影响。由于当前加入的点是一定小于前面点的,所以对于一个段的边缘,直到合并位置,高度都在递减。
而当前插入位置递减的高度与前一本书有关,如果我们以第
i
−
1
i-1
i−1本书的高度为水平线,那么加入第
i
i
i书后水平线将会下降
h
i
−
1
−
h
i
h_{i-1}-h_{i}
hi−1−hi。
我们此时就当所有的段都整体下降,那么它的贡献为
(
2
j
−
k
)
(
h
i
−
1
−
h
i
)
(2j-k)(h_{i-1}-h_{i})
(2j−k)(hi−1−hi)。之后已经连接了的段的位置就不会下降,产生贡献了,只有剩下的两端才会产生贡献。
所以我们就将初始贡献设为
0
0
0,每次加书就看成整体下降产生贡献,这样贡献就只升不降了。
状态定义与先前一样,转移方程式也很好想,主要就是段的合并。
初始高度可以就看成最高书的高度,方便维护。
时间复杂度 O ( n 2 L ) O\left(n^2L\right) O(n2L)。
源码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<set>
using namespace std;
#define MAXN 105
#define lowbit(x) (x&-x)
#define reg register
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int,int> pii;
const int mo=1e9+7;
const double PI=acos(-1.0);
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
int n,L,h[105],dp[2][105][1005][3],ans;
bool cmp(int x,int y){return x>y;}
int add(int x,int y){return x+y<mo?x+y:x+y-mo;}
signed main(){
read(n);read(L);
for(int i=1;i<=n;i++)read(h[i]);
if(n==1){puts("1");return 0;}
sort(h+1,h+n+1,cmp);h[0]=h[1];dp[0][0][0][0]=1;
for(int i=1;i<=n;i++){
int now=i&1,las=now^1,dif=h[i-1]-h[i];
for(int j=1;j<=i;j++)
for(int k=0;k<3;k++){
int tmp=dif*(2*j-k);
for(int t=0;t<=L;t++){
if(t>=tmp-2*dif)dp[now][j][t][k]=add(dp[now][j][t][k],1ll*(j-k)*dp[las][j-1][t-tmp+2*dif][k]%mo);
if(t>=tmp)dp[now][j][t][k]=add(dp[now][j][t][k],1ll*(2*j-k)*dp[las][j][t-tmp][k]%mo);
if(t>=tmp+2*dif)dp[now][j][t][k]=add(dp[now][j][t][k],1ll*j*dp[las][j+1][t-tmp-2*dif][k]%mo);
if(k&&t>=tmp-dif)dp[now][j][t][k]=add(dp[now][j][t][k],1ll*(3-k)*dp[las][j-1][t-tmp+dif][k-1]%mo);
if(k&&t>=tmp+dif)dp[now][j][t][k]=add(dp[now][j][t][k],1ll*(3-k)*dp[las][j][t-tmp-dif][k-1]%mo);
}
}
for(int j=0;j<i;j++)
for(int t=0;t<=L;t++)
for(int k=0;k<3;k++)
dp[las][j][t][k]=0;
}
for(int i=0;i<=L;i++)ans=add(ans,dp[n&1][1][i][2]);
printf("%d\n",ans);
return 0;
}