2019CSP-S Day2T1 Emiya 家今天的饭 题解

2019CSP-S Day2T1 Emiya 家今天的饭 题解

题目链接

我太菜了

  • 64pts, m < = 3 m <= 3 m<=3.

  • 前64pts数据规模都差不多,因为 m m m很小,考虑类似背包的做法,设 f [ i ] [ j ] [ k ] [ l ] f[i][j][k][l] f[i][j][k][l]表示到了第i行,每种食材各用了多少的方案数,转移如下:

    	f[0][0][0][0] = 1;
    	rep(i,1,n)rep(j,0,i)rep(k,0,i)rep(l,0,i)
    	{
    		if(j)f[i][j][k][l] = (f[i][j][k][l] + f[i - 1][j - 1][k][l] * a[i][1]) % mod;
    		if(k)f[i][j][k][l] = (f[i][j][k][l] + f[i - 1][j][k - 1][l] * a[i][2]) % mod;
    		if(l)f[i][j][k][l] = (f[i][j][k][l] + f[i - 1][j][k][l - 1] * a[i][3]) % mod;
    		f[i][j][k][l] = (f[i][j][k][l] + f[i - 1][j][k][l]) % mod;
    	}
    	rep(i,0,n)rep(j,0,n)rep(k,0,n){//统计答案
    		if(i + j + k > n)continue;
    		if(i > j + k)continue;
    		if(j > i + k)continue;
    		if(k > i + j)continue;
    		ans = (ans + f[n][i][j][k]) % mod; 
    	}
    
  • 代码写的比较丑.

  • 88pts,也就是这题的正解,省一该有的分数, m < = 500 m <= 500 m<=500

  • 因为最多有一种食材会超过一半,我们把这种食材和其他剩下的食材分开考虑,设 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示到了第i行,最多的一种食材选了j个,剩下的食材选了k个,合法的情况很多,但不合法的只有 j > k j > k j>k 一种,所以答案是全部的方案数减掉这一种。

  • 转移以及答案统计:

    	rep(i,1,n){
    		rep(j,1,m){
    			scanf("%lld",&a[i][j]);
    			s[i] = (s[i] + a[i][j]) % mod; 
    		}
    	}
    	ans = 1;
    	rep(i,1,n)ans = (ans * (s[i] + 1)) % mod;
    	rep(l,1,m){
    		memset(f,0,sizeof(f));
    		f[0][0][0] = 1;
    		rep(i,1,n)rep(j,0,i)rep(k,0,i){
    			f[i][j][k] += f[i - 1][j][k];
    			if(j) f[i][j][k] += f[i - 1][j - 1][k] * a[i][l];
    			if(k) f[i][j][k] += f[i - 1][j][k - 1] * ((s[i] - a[i][l] + mod) % mod);
    			f[i][j][k] %= mod;
    		}
    		rep(i,1,n)rep(j,0,n - i){
    			if(i > j)ans = (ans - f[n][i][j] + mod) % mod; 
    		}
    	}
    	ans = (ans - 1 + mod) % mod;
    
  • 100pts,这档分我高中没人能拿到, n < = 100 , m < = 2000 n <= 100,m <= 2000 n<=100,m<=2000

  • 刚刚那种做法复杂度为 O ( m n 3 ) O(mn_{}^{3}) O(mn3),满分做法就是动了一下脑子,我们实际关心的只是 j , k j,k j,k的大小关系,所以只需要维护 j − k j - k jk的差值,时间和空间都降下一维,一看就很像标算,大胆细心的写就好。

  • code如下:

    #include <cstdio>
    #include <algorithm>
    #include <iostream>
    #include <set>
    #include <vector>
    #include <cstring>
    #include <cctype>
    #include <map>
    #define ll long long
    #define rep(a,b,c) for(int a = b;a <= c;++a)
    #define per(a,b,c) for(int a = b;a >= c;--a)
    #define N 110
    #define M 2010
    #define mod 998244353
    
    using namespace std;
    
    int n,m;
    ll f[N][N << 1];//到了第i行,最多的为j,其他的为k,j - k + 100的差值 
    ll a[N][M];
    ll s[N];//i行全部的数量 
    ll ans;
     
    int main(){
    	scanf("%d %d",&n,&m);
    	ans = 1;
    	rep(i,1,n){
    		rep(j,1,m){
    			scanf("%lld",&a[i][j]);
    			s[i] = (s[i] + a[i][j]) % mod;
    		}
    		ans = (ans * (s[i] + 1)) % mod;
    	}
    	rep(k,1,m){
    		memset(f,0,sizeof(f));
    		f[0][100] = 1;
    		rep(i,1,n){
    			rep(j,100-i,100+i){
    				f[i][j] = (f[i][j] + f[i - 1][j]) % mod;
    				f[i][j] = (f[i][j] + f[i - 1][j + 1] * ((s[i] - a[i][k] + mod) % mod)) % mod;
    				if(j > 0)f[i][j] = (f[i][j] + f[i - 1][j - 1] * a[i][k]) % mod;
    			}
    		}
    		rep(i,101,100 + n)ans = (ans - f[n][i] + mod) % mod;
    	}
    	ans = (ans - 1 + mod) % mod;
    	printf("%lld\n",ans);
    	return 0; 
    }
    
    

Thanks.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值