6433. 【NOIP2019提高组正式赛day2】Emiya 家今天的饭(meal)

Description

Input

Output

输出到文件 meal.out 中。
仅一行一个整数,表示所求方案数对 998, 244, 353 取模的结果。 

Sample Input

Sample Input1
2 3
1 0 1
0 1 1


Sample Input2
3 3
1 2 3
4 5 0
6 0 0


Sample Input3
5 5
1 0 0 1 1
0 1 0 1 0
1 1 1 1 0 
1 0 1 0 1
0 1 1 0 1
 

Sample Output

Sample Output1
3


Sample Output2
190


Sample Output3
742
 

Data Constraint

Solution

发现当一种菜不合法(即种数大于k/2)时,其它所有的菜都是合法的,因此我们可以用所有的方案减去不合法方案得到答案。

总数就是每一行(总和+1)乘起来-1,表示每一行可以选这么多数也可以不选,但是最后不能一个数都不选。

可以发现不合法方案就是每一列选的个数超过选的总数的一半,因为这一列已经不合法了(超过总数的议案),所以其他列一定是合法的。根据这个性质我们就只用做一次容斥了,那么我们可以设f[i][j][k]表示这一列(设为第l列)的前i行中选了一共选了j行,第l列一共选了k个的方案。

首先f[i][j][k]=f[i-1][j][k](i>1)

如果选a[i][l]这个数,f[i][j][k]+=f[i-1][j-1][k-1]*a[i][l]

若果不选a[i][l]这个数,f[i][j][k]+=f[i-1][j-1][j]*(sum[i]-a[i][l])(这一行其他数的总和)。

最后ans就减去f[n][j][k](k>j/2)就可以了。

时间复杂度是O(mn^3)。

我们考虑如何优化dp,我们发现n是优化不掉的,并且我们只需要k>j/2就可以了,那么其实可以将j和k压为一维dp,将式子化一下,得到:k-j/2>0

2k-j>0

j-2k<0

(j-k)-k<0

观察发现,j-k是除了这一列中其他选的数的个数,而k是这一列的个数,因此,我们相当于用一个新的j'=j-2k表示前i个数中不选这一列的数的个数与选这一列的个数的差为j'时的方案数。

那么f[i][j]就可以从三种递推。

f[i][j]=f[i][j-1]

如果选a[i][l],f[i][j]+=f[i-1][j+1]*a[i][l]。

如果不选a[i][l],f[i][j]+=f[i-1][j-1]*(sum[i]-a[i][l])。

最后我们要减去的答案就是那些f[n][i](i<0)。

这样就能优化到O(mn^2)了。

 Code(84)

#include<cstdio>
#include<cstring>
#include<algorithm>
#define I int
#define ll long long	
#define F(i,a,b) for(I i=a;i<=b;i++)
#define Fd(i,a,b) for(I i=a;i>=b;i--)
#define mem(a,b) memset(a,b,sizeof(a))
#define N 110
#define M 998244353
using namespace std;
I n,m,x;
ll a[N][2010],f[N][N][N],ans=1,s[N];
void rd(I &x){
	x=0;char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
}
void inc(ll &x,ll y){
	x+=y;x-=(x/M)*M;
}
I main(){
	freopen("meal.in","r",stdin);
	freopen("meal.out","w",stdout);
	rd(n),rd(m);
	F(i,1,n){
		F(j,1,m){
			rd(x);a[i][j]=x;
			s[i]+=a[i][j];
		}
		ans=(ans*s[i]+ans)%M;
	}
	--ans;
	F(l,1,m){
		mem(f,0);
		F(i,0,n) f[i][0][0]=f[0][i][0]=1;
		f[1][1][0]=s[1]-a[1][l];
		F(i,2,n){
			f[i][1][0]=f[i-1][1][0]+s[i]-a[i][l];
			F(j,2,i){
				f[i][j][0]=(f[i-1][j][0]+f[i-1][j-1][0]*(s[i]-a[i][l])%M)%M;
			}
		}
		F(k,1,n){
			F(i,k,n){
				F(j,k,i){
					if(i>1) f[i][j][k]=f[i-1][j][k];
					inc(f[i][j][k],f[i-1][j-1][k-1]*a[i][l]);
					inc(f[i][j][k],f[i-1][j-1][k]*(s[i]-a[i][l]));
				}
			}
		}
		F(j,1,n){
			F(k,1,j) if(k>j/2) inc(ans,-f[n][j][k]);
		}
	}
	printf("%lld\n",ans);
	return 0;
}

Code(100)

#include<cstdio>
#include<cstring>
#include<algorithm>
#define I int
#define ll long long	
#define F(i,a,b) for(I i=a;i<=b;i++)
#define Fd(i,a,b) for(I i=a;i>=b;i--)
#define mem(a,b) memset(a,b,sizeof(a))
#define N 110
#define M 998244353
using namespace std;
I n,m,x;
ll a[N][2010],f[N][N*3],ans=1,s[N];
void rd(I &x){
	x=0;char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
}
void inc(ll &x,ll y){
	x+=y;x-=(x/M)*M;
}
I main(){
	freopen("meal.in","r",stdin);
	freopen("meal.out","w",stdout);
	rd(n),rd(m);
	F(i,1,n){
		F(j,1,m){
			rd(x);a[i][j]=x;
//			s[i]+=a[i][j];
			inc(s[i],a[i][j]);
		}
		ans=(ans*s[i]+ans)%M;
	}
	--ans;
	F(l,1,m){
		mem(f,0);
		F(i,1,n) f[i][N]=1;
		f[1][N+1]=s[1]-a[1][l],f[1][N-1]=a[1][l];
		F(i,2,n){
			F(j,-i,i){
				f[i][N+j]=f[i-1][N+j];
				inc(f[i][N+j],f[i-1][N+j+1]*a[i][l]);
				inc(f[i][N+j],f[i-1][N+j-1]*(s[i]-a[i][l]));
			}
		}
		F(i,N-n,N-1) inc(ans,-f[n][i]+M);
	}
	printf("%lld\n",ans);
	return 0;
}


作者:zsjzliziyang 
QQ:1634151125 
转载及修改请注明 
本文地址:https://blog.csdn.net/zsjzliziyang/article/details/103321717

发布了199 篇原创文章 · 获赞 201 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/zsjzliziyang/article/details/103321717