Description
Solution
指向【一大堆常犯的错误、提醒和公式】第21条;
先把所有数排序,从小到大,
K的限制就是,所以组的最大减最小的和
<=K
<script type="math/tex" id="MathJax-Element-6"><=K</script>,
有一个显然的DP:
设f[i][j][k]表示做到i,有j个没有封尾,k为:(已经封尾的组的差)-(没有封尾的组的最小的数),
这样,封尾就直接
k+ai
即可,
转移显然,但复杂度为
O(n2∑ai)
,巨大,开O2才能过,
我们发现,k这一维的状态太大,实际上有用的没有这么多,考虑如何减少,
先差分一下,转移的时候就把k加上
j∗(ai−ai−1)
,也就是只记录做到当前的差为多少,随着转移,差渐渐变大,
这样,k这一位就只有m种状态,大于m的就没用了,
复杂度: O(n2m)
Code
#include <cstdio>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define min(q,w) ((q)>(w)?(w):(q))
#define max(q,w) ((q)<(w)?(w):(q))
using namespace std;
typedef long long LL;
const int N=205,M=1002,mo=1e9+7;
int read(int &n)
{
char ch=' ';int q=0,w=1;
for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
if(ch=='-')w=-1,ch=getchar();
for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;n=q*w;return n;
}
int m,n,ans;
int a[N];
int f[2][N/2][M];
int main()
{
freopen("group.in","r",stdin);
freopen("group.out","w",stdout);
read(n),read(m);
fo(i,1,n)read(a[i]);
sort(a+1,a+1+n);
f[0][0][0]=1;
fo(i,1,n)
{
int I=i&1;int I1=!I,T=min(i,n-i);
fo(j,0,T)
{
int v=min(j*(a[i]-a[i-1]),m+1);
fo(k,0,v-1)f[I][j][k]=0;
fo(k,v,m)
{
f[I][j][k]=(LL)f[I1][j][k-v]*(j+1)%mo;
if((j+1)*(a[i]-a[i-1])<=k)
f[I][j][k]=((LL)f[I][j][k]+(LL)f[I1][j+1][k-(j+1)*(a[i]-a[i-1])]*(j+1))%mo;
}
if(j)
{
v=(j-1)*(a[i]-a[i-1]);
fo(k,v,m)f[I][j][k]=(f[I][j][k]+f[I1][j-1][k-v])%mo;
}
}
}
ans=0;
fo(i,0,m)
{
ans+=f[n&1][0][i];
while(ans>=mo)ans-=mo;
}
printf("%d\n",ans);
return 0;
}