JZOJ5436. 【NOIP2017提高A组集训10.30】Group

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/XLno_name/article/details/83098873

题意:

数据范围:

Analysis:

首先这个肯定先排序,这样好确定最大最小数的差。对于一个组里的差即为最左最右端点。
考虑DP,为表全状态设 f i , j , k f_{i,j,k} 表示做到第 i i 个,还有 j j 组没分好,当前所有组的和为 k k 。因为我们是不断往组里加右端点,所以第三维是递增的。但是每一组的最大最小差并没有解决,我们只有 a i a_i 的信息,并不能推算出转移后 k k 是怎样。
但我们可以这样考虑,若一个组没有被分完,那么其组最后的右端点肯定大于 a i a_i ,那就把 a i a_i 暂时当做右端点。即对于 k k 加上 j ( a i a i 1 ) j*(a_i-a_{i-1}) 就是转移后的和。
另外一种理解方式就是左右端点相减,相当于中间所有距离之和。
那么就差分一下,加上 j ( a i a i 1 ) j*(a_i-a_{i-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;
}

猜你喜欢

转载自blog.csdn.net/XLno_name/article/details/83098873