题解 P5664 【Emiya 家今天的饭【民间数据】】

· · 题解

又是趁着还没有题解,蒟蒻我来讲讲思路……

可以发现,从正面计算每种食材不超过一半的方案数比较困难,于是我们从反面考虑,总的方案数显然是(\prod_{i=1}^n(1+\sum_{j=1}^ma_{i,j}))-1(每种烹饪方式要么有一道菜要么一道也没有,减去什么都不选的一种),而要排除的方案数就是有至少一种食材超过一半的的方案数。

而又因为至多有一种食材超过一半,所以可以转化为:依次求出m种食材各自超过一半的方案数,然后一起减去。

很容易想到\text{DP}。记dp_{i,j,k,l}在前i种烹饪方式中,选出k道菜,使得第j种食材恰有l的方案数。易知

dp_{i,j,k,l}=dp_{i-1,j,k,l}+a_{i,j}dp_{i-1,j,k-1,l-1}+\sum_{1\leq j'\leq m,j'\neq j}a_{i,j'}dp_{i-1,j,k-1,l}

其中上式的三项分别为第i种烹饪方式什么也不选选第j种食材选除了第j种食材的其他食材,大家可以自行验证。记rs_i=\sum_{j=1}^ma_{i,j},于是\sum_{1\leq j'\leq m,j'\neq j}a_{i,j'}=rs_i-a_{i,j},上式转移可以在O(1)内实现,状态总数为O(n^3m),故总时间复杂度为O(n^3m)。最后枚举j,k,l(2l>k)即可。得分84分。(出考场听到有84分我都震惊了)

考虑优化。鉴于转移是O(1)的,考虑优化状态。可以发现,如果要使第j种食材过半,等价于使第j种食材数量大于其他所有食材数量之和,而与上文的k,l无关。于是改进状态设计:

dp_{i,j,k}表示在前i种烹饪方式中,使得第j种食材的数量减去其他食材的数量和的结果恰为k 的方案数(k可为负数,k\in [-n,n])。可写出新的方程

dp_{i,j,k}=dp_{i-1,j,k}+a_{i,j}dp_{i-1,j,k-1}+\sum_{1\leq j'\leq m,j'\neq j}a_{i,j'}dp_{i-1,j,k+1}

其中上式的三项同样分别为第i种烹饪方式什么也不选选第j种食材选除了第j种食材的其他食材。上式转移同样可以在O(1)内实现,状态总数为O(n^2m),故总时间复杂度为O(n^2m)。最后枚举所有j,k(k>0)即可。可以拿到满分。(貌似和上一个做法得分相差不大,可怜我白写了……)

代码蒟蒻我还没拿到,也懒得再打(其实是怕打错然后发慌),就不贴了。

```cpp #include<cstdio> #include<cstring> using namespace std; const long long MOD=998244353; long long a[200][3000],rs[200]; int n=0,m=0; inline int hsh(int x){return x+n+10;} long long dp[200][500]; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { scanf("%lld",&a[i][j]); rs[i]=(rs[i]+a[i][j])%MOD; } } long long sum=1;for(int i=1;i<=n;i++)sum=(sum*(rs[i]+1))%MOD;sum=(sum+MOD-1)%MOD; for(int j=1;j<=m;j++) { memset(dp,0,sizeof(dp)); dp[1][hsh(0)]=1,dp[1][hsh(1)]=a[1][j],dp[1][hsh(-1)]=(rs[1]+MOD-a[1][j])%MOD; for(int i=2;i<=n;i++) { for(int k=-n;k<=n;k++) { dp[i][hsh(k)]=(dp[i-1][hsh(k)]+a[i][j]*dp[i-1][hsh(k-1)]%MOD+(rs[i]+MOD-a[i][j])*dp[i-1][hsh(k+1)]%MOD)%MOD; } } for(int k=1;k<=n;k++)sum=(sum+MOD-dp[n][hsh(k)])%MOD; } printf("%lld",sum); return 0; } ```