JZOJ5436. 【NOIP2017提高A組集訓10.30】Group
阿新 • • 發佈:2018-11-05
題意:
資料範圍:
Analysis:
首先這個肯定先排序,這樣好確定最大最小數的差。對於一個組裡的差即為最左最右端點。
考慮DP,為表全狀態設
表示做到第
個,還有
組沒分好,當前所有組的和為
。因為我們是不斷往組裡加右端點,所以第三維是遞增的。但是每一組的最大最小差並沒有解決,我們只有
的資訊,並不能推算出轉移後
是怎樣。
但我們可以這樣考慮,若一個組沒有被分完,那麼其組最後的右端點肯定大於
,那就把
暫時當做右端點。即對於
加上
就是轉移後的和。
另外一種理解方式就是左右端點相減,相當於中間所有距離之和。
那麼就差分一下,加上
。(差分是套路要記住,很多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;
}