CSP-S2019 D2T1 Emiya家今天的饭

CSP-S2019 D2T1 Emiya家今天的饭

洛谷传送门

题目描述

Emiya 是个擅长做菜的高中生,他共掌握 nn烹饪方法,且会使用 mm主要食材做菜。为了方便叙述,我们对烹饪方法从 1 sim n1∼n 编号,对主要食材从 1 sim m1∼m 编号。

Emiya 做的每道菜都将使用恰好一种烹饪方法与恰好一种主要食材。更具体地,Emiya 会做 a_{i,j}a**i,j 道不同的使用烹饪方法 ii 和主要食材 jj 的菜(1 leq i leq n, 1 leq j leq m1≤in,1≤jm),这也意味着 Emiya 总共会做 sumlimits_{i=1}^{n} sumlimits_{j=1}^{m} a_{i,j}i=1∑n**j=1∑mai,j 道不同的菜。

Emiya 今天要准备一桌饭招待 Yazid 和 Rin 这对好朋友,然而三个人对菜的搭配有不同的要求,更具体地,对于一种包含 kk 道菜的搭配方案而言:

  • Emiya 不会让大家饿肚子,所以将做至少一道菜,即 k geq 1k≥1
  • Rin 希望品尝不同烹饪方法做出的菜,因此她要求每道菜的烹饪方法互不相同
  • Yazid 不希望品尝太多同一食材做出的菜,因此他要求每种主要食材至多在一半的菜(即 lfloor frac{k}{2} floor⌊2k⌋ 道菜)中被使用

这里的 lfloor x floor⌊x⌋ 为下取整函数,表示不超过 xx 的最大整数。

这些要求难不倒 Emiya,但他想知道共有多少种不同的符合要求的搭配方案。两种方案不同,当且仅当存在至少一道菜在一种方案中出现,而不在另一种方案中出现。

Emiya 找到了你,请你帮他计算,你只需要告诉他符合所有要求的搭配方案数对质数 998,244,353998,244,353 取模的结果。

输入格式

第 1 行两个用单个空格隔开的整数 n,mn,m

第 2 行至第 n + 1n+1 行,每行 mm 个用单个空格隔开的整数,其中第 i + 1i+1 行的 mm 个数依次为 a_{i,1}, a_{i,2}, cdots, a_{i,m}a**i,1,a**i,2,⋯,a**i,m

输出格式

仅一行一个整数,表示所求方案数对 998,244,353998,244,353 取模的结果

说明/提示

【样例 1 解释】

由于在这个样例中,对于每组 i, ji,j,Emiya 都最多只会做一道菜,因此我们直接通过给出烹饪方法、主要食材的编号来描述一道菜。

符合要求的方案包括:

  • 做一道用烹饪方法 1、主要食材 1 的菜和一道用烹饪方法 2、主要食材 2 的菜
  • 做一道用烹饪方法 1、主要食材 1 的菜和一道用烹饪方法 2、主要食材 3 的菜
  • 做一道用烹饪方法 1、主要食材 3 的菜和一道用烹饪方法 2、主要食材 2 的菜

因此输出结果为 3 mod 998,244,353 = 33mod998,244,353=3。 需要注意的是,所有只包含一道菜的方案都是不符合要求的,因为唯一的主要食材在超过一半的菜中出现,这不满足 Yazid 的要求。

【样例 2 解释】

Emiya 必须至少做 2 道菜。

做 2 道菜的符合要求的方案数为 100。

做 3 道菜的符合要求的方案数为 90。

因此符合要求的方案数为 100 + 90 = 190。

【数据范围】

测试点编号 n=n= m=m= a_{i,j}<a**i,j< 测试点编号 n=n= m=m= a_{i,j}<a**i,j<
11 22 22 22 77 1010 22 10^3103
22 22 33 22 88 1010 33 10^3103
33 55 22 22 9sim 129∼12 4040 22 10^3103
44 55 33 22 13sim 1613∼16 4040 33 10^3103
55 1010 22 22 17sim 2117∼21 4040 500500 10^3103
66 1010 33 22 22sim 2522∼25 100100 2 imes 10^32×103 998244353998244353

对于所有测试点,保证 1 leq n leq 1001≤n≤100,1 leq m leq 20001≤m≤2000,0 leq a_{i,j} lt 998,244,3530≤a**i,j<998,244,353。


题解:

D1T1、D2T1和D1T2是考场上唯三动手写了的题,其他的都没什么思路(蒟蒻太菜了没办法)。

但是最终只有D1T1切掉了,还挂了5分见祖宗。其他的两道题部分分都没拿满,最终弱省省二收场。

主要还是太菜,这道题的暴搜都没写出来。

不多回忆了,开讲。

分析题意:

说了一堆废话,提炼出来就是:

有一个(N)(M)列的矩阵,至少选一行,每行最多选一种,每列最多选(lfloorfrac{k}{2} floor)种。统计方案数模上998244353。

注意是种而不是个,所以在计数的时候需要使用乘法原理,当前种类的(a_{i,j})都要×进去。

一看部分分,发现有32分的暴搜(就是蒟蒻考场上想写的),也就是每行可以有4种选择:食材1、2、3以及不选。总复杂度是(O(4^{10})),可过。

然后就是这个过程的递归模拟...考码力了...(莫名想哭,这32拿到了我就省一了啊)

首先,我们想到的是递归到最后一层,然后判断答案是否合法。合法的话就统计进去,不合法的话就不统计。但是这样的方式只能统计到(k=n)的情况,对(k<n)的就不太可做。

所以我们不如把选几个作为参量往里传,这样的话思路会明晰很多。

所以蒟蒻又来总结了:搜索算法重要的是递归出口,以及在递归过程中维护自己想要的信息。这个信息的维护,如果发现当前过程比较难维护,莫不如尝试着把它归到递归函数的参量里。比如这道题,我的思路过程已经在上面展示了。

32p代码:

#include<cstdio>
#define ll long long
using namespace std;
const int mod=998244353;
int n,m;
int a[20][5],cnt[5];//cnt[i]表示第i种食材一共做了几道菜
ll ans;
ll b[20];//b[i]表示第i道菜
void dfs(int dep,ll tot,int x,int y)
//表示当前进行到第dep种烹饪方法,方案数为tot,要选x道菜,已经选了y道菜
{
    if(x==y)
    {
        ans=(ans+tot)%mod;
        return;
    }
    if(dep>n)
        return;
    for(int i=1;i<=m;i++)
        if(cnt[i]+1<=x/2)
        {
            cnt[i]++;
            dfs(dep+1,tot*a[dep][i]%mod,x,y+1);
            cnt[i]--;
        }
    dfs(dep+1,tot,x,y);
}
int 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]);
    for(int i=1;i<=n;i++)
        dfs(0,1,i,0);
    printf("%lld",ans);
    return 0;
}

然后我们开始深入思考。

其实考场上想到了DP,因为计数类问题我们其实也拿不出什么其他的算法了(本蒟蒻肤浅之言)

Code:

#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const int maxn=110,maxm=2010,mod=998244353;
int n,m;
ll ans=1;
ll cnt[maxn],w[maxn][maxm],dp[maxn][maxm];
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
			scanf("%lld",&w[i][j]),cnt[i]=(cnt[i]+w[i][j])%mod;
		ans=(ans*(cnt[i]+1))%mod;
	}
	ans=(ans+mod-1)%mod;
	for(int i=1;i<=m;i++)
	{
		memset(dp,0,sizeof(dp));
		dp[0][0]=1;
		for(int j=1;j<=n;j++)
			for(int k=0;k<=2*(j-1);k++)
			{
				dp[j][k]=(dp[j][k]+dp[j-1][k]*(cnt[j]-w[j][i]))%mod;
				dp[j][k+1]=(dp[j][k+1]+dp[j-1][k])%mod;
				dp[j][k+2]=(dp[j][k+2]+dp[j-1][k]*w[j][i])%mod;
			}
		for(int j=n+1;j<=2*n;j++)
			ans=(ans+mod-dp[n][j])%mod;
	}
	printf("%lld",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/fusiwei/p/13689193.html