题意:
数据范围:
Analysis:
首先这个肯定先排序,这样好确定最大最小数的差。对于一个组里的差即为最左最右端点。
考虑DP,为表全状态设
f
i
,
j
,
k
f_{i,j,k}
fi,j,k表示做到第
i
i
i个,还有
j
j
j组没分好,当前所有组的和为
k
k
k。因为我们是不断往组里加右端点,所以第三维是递增的。但是每一组的最大最小差并没有解决,我们只有
a
i
a_i
ai的信息,并不能推算出转移后
k
k
k是怎样。
但我们可以这样考虑,若一个组没有被分完,那么其组最后的右端点肯定大于
a
i
a_i
ai,那就把
a
i
a_i
ai暂时当做右端点。即对于
k
k
k加上
j
∗
(
a
i
−
a
i
−
1
)
j*(a_i-a_{i-1})
j∗(ai−ai−1)就是转移后的和。
另外一种理解方式就是左右端点相减,相当于中间所有距离之和。
那么就差分一下,加上
j
∗
(
a
i
−
a
i
−
1
)
j*(a_i-a_{i-1})
j∗(ai−ai−1)。(差分是套路要记住,很多DP都可以利用差分来得到一些特殊的性质)
转移只要分:是否开新的组,是否直接分出一个新的组,是否作为旧组结尾来转移就好了
Code:
# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std;
const int N = 2e2 + 5;
const int M = 1e3 + 5;
const int mo = 1e9 + 7;
typedef long long ll;
int f[2][N][M],a[N];
int n,k;
inline int inc(int x,int y) { return x + y >= mo ? x + y - mo : x + y; }
int main()
{
freopen("group.in","r",stdin);
freopen("group.out","w",stdout);
scanf("%d%d",&n,&k);
for (int i = 1 ; i <= n ; ++i) scanf("%d",&a[i]);
sort(a + 1,a + n + 1); int nx = 0; f[0][0][0] = 1;
for (int i = 0 ; i < n ; ++i,nx ^= 1)
{
int d = a[i + 1] - a[i];
memset(f[nx ^ 1],0,sizeof(f[nx ^ 1]));
for (int j = 0 ; j <= i ; ++j)
for (int l = 0 ; l <= k ; ++l)
if (f[nx][j][l])
{
int v = l + d * j; if (v > k) break;
f[nx ^ 1][j + 1][v] = inc(f[nx ^ 1][j + 1][v],f[nx][j][l]);
f[nx ^ 1][j][v] = inc(f[nx ^ 1][j][v],f[nx][j][l]);
if (j) f[nx ^ 1][j][v] = inc(f[nx ^ 1][j][v],(ll)f[nx][j][l] * j % mo);
if (j) f[nx ^ 1][j - 1][v] = inc(f[nx ^ 1][j - 1][v],(ll)f[nx][j][l] * j % mo);
}
}
int ans = 0;
for (int i = 0 ; i <= k ; ++i) ans = inc(ans,f[nx][0][i]);
printf("%d\n",ans);
return 0;
}