zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    Pig设计模式概要以及与SQL的设计模式的对比
    Pig设计模式概要以及与SQL的设计模式的对比
    ArcGIS API for Silverlight 学习笔记
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
  • 原文地址:https://www.cnblogs.com/fusiwei/p/13689193.html
Copyright © 2011-2022 走看看