zoukankan      html  css  js  c++  java
  • 牛客52 烹饪

    题意

    (N)种食材,每种食材有无限个,并且每种食材都有一个美味值,记为(a_i)

    请你选出若干个食材,使这些食材相互组合可以烹饪出任意正整数美味值的菜肴

    菜肴的烹饪过程如下:

    • 初始时菜肴的美味值为(0)
    • 每次加入一种食材,可以选择让菜肴的美味值上升(a_i)或下降(a_i)

    请求出符合条件的食材挑选方案个数,一个食材方案个数是一个不可重无序集合((b_1,b_2...b_m))表示从原来的(n)种食材中挑选了(m)种食材,并且可以用这(m)种食材烹饪出任意美味度的菜肴

    取模(998244353)

    (Nleq 3000,a_ileq 2000)


    解法

    观察到题目给出的条件很弱,考虑把条件强化为好求出的限制

    很容易发现的是,能表示出任意正整数 (leftrightarrow) 能表示出1

    而什么样的数列集合能表示出(1)呢?

    从简单的开始考虑,若只有两个数(u,v),它能表示出(1)的充要条件是((u,v)=1)

    由于(u,v)互质,那么(u,v)(u)的整数倍形成的集合模(v)的剩余系中一定有(1),也就一定能表示出(1)

    推广到长度为(n)的情况,我们能发现,如果((a_1,a_2...a_n)=1),那么这个序列就是合法的

    这样我们又把能表示出1这一条件转化为了最大公因数为1这一更加强的条件

    我们的任务实际上转化为了求(gcd=1)的子序列个数

    这个用DP和容斥都可以求,这里写一下DP做法

    (f[i][j])为前(i)个数中(gcd=j)的子序列个数,转移时讨论(a_j)加入或不加入就可以了,最后的答案即为(f[N][1])


    代码

    #include <cstdio>
    
    using namespace std;
    
    const int MAX_N = 3010;
    const int mod = 998244353;
    
    int N;
    
    int a[MAX_N], f[MAX_N][MAX_N];
    
    inline void add(int& x, int y) { x = (x + y) % mod; }
    inline int gcd(int x, int y) { return !y ? x : gcd(y, x % y); }
    
    int main() {
    	
    	scanf("%d", &N);
    	for (int i = 1; i <= N; ++i)  scanf("%d", a + i);
    	
    	for (int i = 1; i <= N; ++i) {
    		f[i][a[i]] = 1;
    		for (int j = 1; j <= 2000; ++j) {
    			add(f[i][j], f[i - 1][j]);
    			add(f[i][gcd(j, a[i])], f[i - 1][j]);
    		}
    	}
    	
    	printf("%d
    ", f[N][1]);
    	
    	return 0;
    }
    
  • 相关阅读:
    java8学习之Optional深入详解
    java8学习之Supplier与函数式接口总结
    java8学习之Predicate深入剖析与函数式编程本质
    conda
    matplotlib-折线图、散点图
    欧盟GDPR通用数据保护条例-原文
    python装饰器的应用案例
    python练习汇总
    python分段算利润、税收
    需求-shidebing
  • 原文地址:https://www.cnblogs.com/VeniVidiVici/p/11626374.html
Copyright © 2011-2022 走看看