Luogu5664 Emiya家今天的饭 - 容斥 - dp -

题目链接:https://www.luogu.com.cn/problem/P5664

题解:
首先注意到至多有一个食材可能超过一半
不妨枚举这个食材,然后统计其超过一半时的方案数,最后容斥一下减去即可

一个比较初级的想法是钦定了第col列,设\(dp[i][j][k]\)表示考虑到第 i 行,第 col 列选了 j 个,其它列选了 k 个的方案数,转移就枚举当前行选/不选
时间复杂度是\(O(mn^3)\)的,可以有84pts

然后我们发现实际上我们只关心 j 和 k的相对大小(一半的限制也是为此),于是我们可以压缩状态 \(dp[i][p]\) 表示考虑到第 i 行,上面的j-k+100(去掉负数) = p的方案数,其余同理
时间复杂度\(O(mn^2)\)
别忘了减去什么都不选的情况!

84pts:

// by SkyRainWind
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
#define pii pair<int,int>

using namespace std;

typedef long long LL;

const int inf = 1e9, INF = 0x3f3f3f3f, mod = 998244353;

int n,m;
int a[42][502],dp[42][42][42], s[105];
void upd(int &x,int y){(x += y) %= mod;}

signed main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)scanf("%d",&a[i][j]), upd(s[i], a[i][j]);
	int r1 = 0;
	for(int col = 1;col<=m; col ++){
		dp[0][0][0] = 1;
		for(int i=1;i<=n;i++){
			for(int j=0;j<=i;j++){
				for(int k=0;j+k<=i;k++){
					dp[i][j][k] = dp[i-1][j][k];
					if(j)upd(dp[i][j][k], 1ll * dp[i-1][j-1][k] * a[i][col] % mod);
					if(k)upd(dp[i][j][k], 1ll * dp[i-1][j][k-1] * (mod + s[i] - a[i][col])%mod);
				}
			}
		}
		for(int i=0;i<=n;i++)
			for(int j=0;i+j<=n;j++){
				if(i > (i+j)/2){
					upd(r1, dp[n][i][j]);
				}
			}
	}
	LL ans = 1;
	for(int i=1;i<=n;i++)ans = 1ll * ans * (s[i] + 1) % mod;
	printf("%d\n",(mod + ans - r1 - 1) % mod);

	return 0;
}

100pts:

// by SkyRainWind
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
#define pii pair<int,int>

using namespace std;

typedef long long LL;

const int inf = 1e9, INF = 0x3f3f3f3f, mod = 998244353, bs = 100;

int n,m;
int a[102][2002],dp[102][202], s[105];
void upd(int &x,int y){(x += y) %= mod;}

signed main(){
//	freopen("meal.in","r",stdin);
//	freopen("meal.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)scanf("%d",&a[i][j]), upd(s[i], a[i][j]);
	int r1 = 0;
	for(int col = 1;col<=m; col ++){
		dp[0][bs] = 1;
		for(int i=1;i<=n;i++){
			for(int j=bs-i;j<=bs+i;j++){
				dp[i][j] = dp[i-1][j];
				if(j)upd(dp[i][j], 1ll * dp[i-1][j-1] * a[i][col] % mod);
				upd(dp[i][j], 1ll * dp[i-1][j+1] * (mod + s[i] - a[i][col])%mod);
			}
		}
		for(int i=bs+1;i<=200;i++)upd(r1, dp[n][i]);
	}
	LL ans = 1;
	for(int i=1;i<=n;i++)ans = 1ll * ans * (s[i] + 1) % mod;
	printf("%d\n",(mod + ans - r1 - 1) % mod);

	return 0;
}
posted @ 2022-10-11 15:42  SkyRainWind  阅读(10)  评论(0编辑  收藏  举报