把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷5664】Emiya 家今天的饭(容斥+DP)

点此看题面

  • \(n\)种烹饪方法和\(m\)种食材,用第\(i\)种烹饪方法烹饪第\(j\)种食材有\(a_{i,j}\)种方法。
  • 现在可以做任意道菜(假设做了\(k\)道),满足:
    • 至少一道菜。
    • 每道菜烹饪方法各不相同。
    • 一种食材至多出现在\(\lfloor\frac k2\rfloor\)道菜中。
  • \(n\le100,m\le2\times10^3\)

去年联赛的题目。。。

仔细研究了一下去年考场上的代码,发现和我现在的想法差不多,看来实力并没有太大退步啊。

于是开开心心地来补博客啦!

容斥

发现一种食材至多出现\(\lfloor\frac k2\rfloor\)中,那么如果我们枚举一种不符合限制(超过\(\lfloor\frac k2\rfloor\))的食材,显然不可能有大于等于两种食材超过限制。

总方案数随便\(DP\)一下就好了。

至于不合法方案,枚举一种食材之后,设\(g_{i,p}\)表示考虑了前\(i\)种烹饪方法,当前枚举食材与其他食材数量之差为\(p\)的方案数。

转移时就分选择当前食材、选择其他食材两种,最后减去\(p>0\)的贡献就好了。

代码:\(O(n^2m)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100
#define M 2000
#define X 998244353
#define Inc(x,y) ((x+=(y))>=X&&(x-=X))
using namespace std;
int n,m,a[N+5][M+5],s[N+5],f[N+5][N+5],g[N+5][2*N+5];
I void read(int& x) {char c;x=0;W(!isdigit(c=getchar()));W(x=x*10+(c&15),isdigit(c=getchar()));}
int main()
{
	RI i,j,k,p,q,ans=0;read(n),read(m);
	for(i=1;i<=n;++i) for(j=1;j<=m;++j) read(a[i][j]),Inc(s[i],a[i][j]);//统计每种烹饪方法各食材方案总数
	for(f[0][0]=1,i=1;i<=n;++i) for(j=0;j<=n;++j) f[i][j]=(1LL*f[i-1][j-1]*s[i]+f[i-1][j])%X;//转移类似组合数
	for(i=1;i<=n;++i) Inc(ans,f[n][i]);//总方案数
	for(j=1;j<=m;++j)//枚举不符合限制的食材
	{
		for(g[0][n]=i=1;i<=n;++i) for(p=n/2;p<=2*n;++p)//数组下标不能为负,第二维统一加n
			g[i][p]=(1LL*g[i-1][p-1]*a[i][j]%X+1LL*g[i-1][p+1]*(s[i]-a[i][j]+X)%X+g[i-1][p])%X;//选择当前枚举食材;选择其他食材
		for(p=n+1;p<=2*n;++p) Inc(ans,X-g[n][p]);//减去p>0的贡献
	}
	return printf("%d",ans),0;//输出答案
}
posted @ 2020-11-04 20:13  TheLostWeak  阅读(53)  评论(0编辑  收藏  举报