【NOIP2017提高A组集训10.30】Group

来源:互联网 发布:mysql分类查询 编辑:程序博客网 时间:2024/05/22 17:02

Description:

这里写图片描述
1<=n<=200,1<=k<=1000

题解:

首先肯定得排个序,再考虑如何dp。
一个组的贡献可以是最大值减最小值,也可以是相邻的两个数的差的和。
这样就可以dp了,对于一个组,如果它没有结尾,那么每往后移以下,贡献就会增加。
可以设fi,j,k表示当前做到第i个数,有j个组还没有结尾,贡献和已经是k的方案数。
对于新来的一个数,有4种情况:
1.新开一组但不做结尾。
2.新开一组且做结尾。
3.接到已有的一组后但是不做结尾。
4.接到已有的一组后并做结尾。

根据这4种情况可得方程。

Code:

#include<cstdio>#include<algorithm>#include<cstring>#define ll long long#define fo(i, x, y) for(int i = x; i <= y; i ++)using namespace std;const int mo = 1e9 + 7;const int N = 201, M = 1001;int n, k, o, a[N], f[2][N][M];int main() {    freopen("group.in", "r", stdin);    freopen("group.out", "w", stdout);    scanf("%d %d", &n, &k);    fo(i, 1, n) scanf("%d", &a[i]);    sort(a + 1, a + n + 1);    f[o][1][0] = f[o][0][0] = 1;    fo(i, 2, n) {        o = !o;        memset(f[o], 0, sizeof f[o]);        fo(j, 0, i - 1) {            fo(p, 0, k) {                int v = p + j * (a[i] - a[i - 1]);                if(v > k) break;                f[o][j + 1][v] += f[!o][j][p]; f[o][j + 1][v] %= mo;                f[o][j][v] += f[!o][j][p]; f[o][j][v] %= mo;                if(j) f[o][j][v] += (ll)f[!o][j][p] * j % mo, f[o][j][v] %= mo;                if(j) f[o][j - 1][v] += (ll)f[!o][j][p] * j % mo, f[o][j - 1][v] %= mo;            }        }    }    int ans = 0;    fo(p, 0, k) ans = (ans + f[o][0][p]) % mo;    printf("%d", ans);}
原创粉丝点击