1 条题解

  • 0
    @ 2021-8-21 17:08:54
    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int N=505,M=1e3+5,P=1e9+7;
    int n,m,ans;
    int s[N];
    int f[2][N][M]; 
    //对于i个学生分了j组不和谐度之和为k的方案数为f[i][j][k] 
    //update i个学生目前还有j组可以加入,总不和谐度是k的方案数 
    void add(int &x,ll y){x=(x+y)%P;}
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;++i)
            scanf("%d",&s[i]);
        sort(s+1,s+1+n);
        f[1][0][0]=f[1][1][0]=1;
    	//初始化 1个人的时候只有一种可能
    	//f[i][0][k] 已考虑i个人不和谐度为k时所有分组方案之和 
    	//本质是所有组都已经关闭,即分完组 
    	for(int i=2;i<=n;++i)
    	{
    	    int now=i&1;
    	    memset(f[now],0,sizeof(f[now]));
    		for(int j=0;j<i;++j)
    		{
    			int delta=j*(s[i]-s[i-1]);
    			//用差分的形式保证了不和谐度与当前组内的最小s没有关系
    			//对于j的解释: 
    			//目前有j个组正在开放,所以至少要再进入j个更大的数
    			//在j个组都封闭后,考虑差分,必然会对k产生j倍贡献 
    			for(int k=0;k+delta<=m;++k)
    			{
    				int sum=k+delta;
    				//判断到达性 
    				if(!f[now^1][j][k]) continue;
    				//保证第i个学生是最慢的 
    				//所以要再开放一个组  
    				add(f[now][j+1][sum],f[now^1][j][k]);
    				//保证第i个学生是某个组里最快的 
    				//所以要封闭一个组 
    				if(j)
    				    add(f[now][j-1][sum],1ll*f[now^1][j][k]*j);
    			    //保证第i个学生是某个组里的中间 
    			    //对于(j+1)的解释 j是可以在j组选择中任选一组 
    			    //+1是因为可以先自己新开一组,然后马上自闭 
    				add(f[now][j][sum],1ll*f[now^1][j][k]*(j+1));
    			}	
    		}
        }
    	for(int i=0,now=n&1;i<=m;++i)
    	    add(ans,f[now][0][i]); 
        printf("%d",ans);
    	return 0;
    }
    
    
    • 1

    信息

    ID
    459
    时间
    1000ms
    内存
    256MiB
    难度
    8
    标签
    递交数
    16
    已通过
    7
    上传者