[CSP2019] Emiya 家今天的饭

题目

点这里看题目。

分析

感觉比往年的 NOIP 的 D2T1 更难。不过看看 D1T3 也就觉得挺合理了。

32pts

暴力搜索不多说,时间 (O(m(m+1)^n)) ,其中的 (O(m)) 用于检查。

64pts

这是考场上的思路,想了大概 10 min 不到。

针对 (m) 很小的数据,直接把 (m) 种食材的使用次数全部塞到状态里面。以 (m=2) 为例子,可以有如下状态:

(f(i,j,k)):前 (i) 种烹饪方法,用了 (j) 次第一种食材, (k) 次第二种食材的方案数。

转移很显然:

[f(i,j,k)=f(i-1,j,k)+a_{i,1} imes f(i-1,j-1,k)+a_{i,2} imes f(i-1,j,k-1) ]

最后统计一下答案,应该长这个样子:

[sum_{k=1}^nsum_{j=1}^{lfloorfrac k 2 floor} f(n,j,k-j)[k-jle lfloorfrac k 2 floor] ]

时间复杂度为 (O(n^{m+1}))

84pts

考场上想容斥的时候搞忘了(le lfloorfrac k 2 floor)的条件,直接导致暴毙

32pts 和 64pts 都是正向思考,尝试满足条件,但是我们似乎已经走到了尽头。

很自然地,我们考虑逆向思考,也就是容斥,考虑不满足条件。

题目有用的限制也就这么一个:

[ ext{每种主要食材出现次数}lelfloorfrac{k}{2} floor ]

我们就反其道而行之,考虑,如果一种主要食材出现次数 (>lfloorfrac k 2 floor) 会发生什么?

这就意味着,其它主要食材出现总次数(le lfloorfrac k 2 floor),也就意味着,最多只会有一种食材不满足限制,我们可以枚举不满足限制的食材进行容斥。

上述性质顺便也说明了,钦定不合法食材的数量一定大于其他食材的总数,这就便于设计状态进行 DP :

(f(i,j,k)):前 (i) 种烹饪方法,使用了 (j) 次钦定食材, (k) 次其他食材的方案数。

设钦定食材编号为 (u)(s_i) 为使用第 (i) 种烹饪方法能做出的菜的总数。

可以类比 64pts 给出的转移方程:

[f(i,j,k)=f(i-1,j,k)+a_{i,u} imes f(i-1,j-1,k)+(s_i-a_{i,u}) imes f(i-1,j,k-1) ]

这样做是 (O(n^3m)) 的。

100pts

上述方法会 T 掉后 4 个大点,还需优化。

我们感觉已经很接近正解了。外层容斥枚举无法优化,因而考虑优化内层的 DP 。

我们想要知道的只是数量关系,并不想要实际数量。而数量关系可以通过运算与运算结果来表达。

典型的例子:(j>kLeftrightarrow j-k>0)

利用这样的性质,我们可以优化状态:

(g(i,j)):前 (i) 种烹饪方法,钦定食材比其他食材多用 (j)的方案数。

转移也很简单:

[g(i,j)=g(i-1,j)+a_{i,u} imes g(i-1,j-1)+(s_i-a_{i,u}) imes g(i-1,j+1) ]

需要注意 (j) 可以取到负数

时间复杂度是 (O(n^2m)),足以通过本题。

代码

32pts

int cnt[MAXM];
int a[MAXN][MAXM];
LL ans; int N, M;

void DFS( const int ind, const LL way, const int tot )
{
	if( ! way ) return ;
	if( ind > N ) 
	{
		for( int i = 1 ; i <= M ; i ++ )
			if( cnt[i] > tot >> 1 )
				return ;
		( ans += way ) %= mod;
		return ;
	}
	DFS( ind + 1, way, tot );
	for( int i = 1 ; i <= M ; i ++ )
	{
		cnt[i] ++; 
		DFS( ind + 1, 1ll * way * a[ind][i] % mod, tot + 1 ); 
		cnt[i] --;
	}
}

64pts

这里以 (m=2) 的情况为例, (m=3) 的请自行斟酌。

    f[0][0] = 1;
    for( int i = 1 ; i <= N ; i ++ )
        for( int j = i ; ~ j ; j -- )
            for( int k = i - j ; ~ k ; k -- )
            {
                if( j ) add( f[j][k], 1ll * f[j - 1][k] * a[i][1] % mod );
                if( k ) add( f[j][k], 1ll * f[j][k - 1] * a[i][2] % mod );
            }
    int ans = 0;
    for( int k = 1 ; k <= N ; k ++ )
        for( int j = 1 ; j <= ( k >> 1 ) ; j ++ )
            if( k - j <= ( k >> 1 ) )
                add( ans, f[j][k - j] );

84pts

int f[MAXM][MAXM];
int a[MAXN][MAXM], s[MAXN];
int N, M;

void add( int &x, const int v ) { x = ( x + v >= mod ? x + v - mod : x + v ); }

int calc( const int imp )
{
	for( int j = 0 ; j <= N ; j ++ )
		for( int k = 0 ; k <= N ; k ++ )
			f[j][k] = 0;
	f[0][0] = 1;
	for( int i = 1 ; i <= N ; i ++ )
		for( int j = i ; ~ j ; j -- )
			for( int k = i - j ; ~ k ; k -- )
			{
				if( j ) add( f[j][k], 1ll * f[j - 1][k] * a[i][imp] % mod );
				if( k ) add( f[j][k], 1ll * f[j][k - 1] * ( s[i] - a[i][imp] ) % mod ); 
			}
	int ret = 0;
	for( int k = 1 ; k <= N ; k ++ )
		for( int j = ( k >> 1 ) + 1 ; j <= k ; j ++ )
			add( ret, f[j][k - j] );
	return ret;
}

int main()
{
	int all = 1;
	read( N ), read( M );
	for( int i = 1 ; i <= N ; i ++ )
	{
		for( int j = 1 ; j <= M ; j ++ )
			read( a[i][j] ), add( s[i], a[i][j] );
		all = 1ll * all * ( s[i] + 1 ) % mod;
	}
	all = ( all - 1 + mod ) % mod;
	for( int i = 1 ; i <= M ; i ++ ) 
		all = ( all - calc( i ) + mod ) % mod;
	write( all ), putchar( '
' );
	return 0;
}

100pts

int& F( const int i, const int j ) { return f[i][j + N]; }
void add( int &x, const int v ) { x = ( x + v >= mod ? x + v - mod : x + v ); }

int calc( const int imp )
{
	for( int i = 0 ; i <= N ; i ++ )
		for( int j = -N ; j <= N ; j ++ )
			F( i, j ) = 0;
	F( 0, 0 ) = 1;
	for( int i = 1 ; i <= N ; i ++ )
		for( int j = -i ; j <= i ; j ++ )
		{
			F( i, j ) = F( i - 1, j );
			if( j > -i ) add( F( i, j ), 1ll * a[i][imp] * F( i - 1, j - 1 ) % mod );
			if( j < i ) add( F( i, j ), 1ll * ( s[i] - a[i][imp] + mod ) * F( i - 1, j + 1 ) % mod );
		}
	int ret = 0;
	for( int j = 1 ; j <= N ; j ++ )
		add( ret, F( N, j ) );
	return ret;
}
原文地址:https://www.cnblogs.com/crashed/p/13372918.html