发现直接做非常困难,考虑将能力值排序然后从小到大插入。
令 (f_{i,j,l}) 表示前 (i) 个人,有 (j) 组需要继续加人,不和谐度之和至少为 (l) 的方案数。此时加入一个人产生的贡献为 (t=j imes(a_{i+1}-a_i))。分三种情况转移:
- 组数加一,即第 (i+1) 个人在一个新的组中,然后该组还需要继续加人。[f_{i+1,j+1,l+t}gets f_{i+1,j+1,l+t}+f_{i,j,l} ]
- 组数不变,第 (i+1) 个人在一个新的组中,然后该组不需要继续加人,或加入了一个原有的组,然后该组还需要继续加人。[f_{i+1,j,l+t}gets f_{i+1,j,l+t}+f_{i,j,l} imes(j+1) ]
- 组数减一,即第 (i+1) 个人加入了一个原有的组,然后该组不需要继续加人。[f_{i+1,j-1,l+t}gets f_{i+1,j-1,l+t}+j imes f_{i,j,l} ]
最后的答案就是 (sumlimits_{l=0}^k f_{n,0,l})。
其实这道题无论是预处理还是 DP 部分和 JOI2012 的袋鼠都十分相似,都是通过排序来确定每次产生的贡献(变化),状态都是记录遗留下来需要继续做的量。
时间复杂度 (O(n^2k))。
code:
#include<bits/stdc++.h>
using namespace std;
#define N 205
#define Mod 1000000007
#define For(i,x,y)for(i=x;i<=(y);i++)
int f[N][N][1005],a[N];
int main()
{
int n,k,i,j,l,ans=0,t;
cin>>n>>k;
For(i,1,n)cin>>a[i];
sort(a+1,a+n+1);
f[0][0][0]=1;
For(i,0,n-1)
For(j,0,i)
For(l,0,k)
{
t=l+j*(a[i+1]-a[i]);
if(t<=k)
{
f[i+1][j+1][t]=(f[i+1][j+1][t]+f[i][j][l])%Mod;
//作为一组的开头
f[i+1][j][t]=(f[i+1][j][t]+1LL*f[i][j][l]*(j+1))%Mod;
//单独作为一组或接到一组后面
if(j)f[i+1][j-1][t]=(f[i+1][j-1][t]+1LL*j*f[i][j][l])%Mod;
//作为一组的末尾
}
else break;
}
For(l,0,k)ans=(ans+f[n][0][l])%Mod;
cout<<ans;
return 0;
}