您的位置:首页 > 其它

【JZOJ 5436】【NOIP2017提高A组集训10.30】Group

2017-10-30 22:03 288 查看

Description



Solution

指向【一大堆常犯的错误、提醒和公式】第21条;

先把所有数排序,从小到大,

K的限制就是,所以组的最大减最小的和<=K,

有一个显然的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
;
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: