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 j−k的差值,时间和空间都降下一维,一看就很像标算,大胆细心的写就好。
-
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; }