链接:https://ac.nowcoder.com/acm/contest/1084/C
来源:牛客网
烹饪
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
“你已经是一个成熟的孩子了,要学会自己烹饪了!”
小 Y 上山拜师学艺,经过 年之长的厨艺练习,已成为当世名厨,今天他接受邀请,在众人面前展示自己高超的厨艺。
人们给小 Y 提供了 种食物,每种食物无限量供应,每种食物都有一个美味值,记为 aia_iai。
小 Y 为了展示他的厨艺,他需要挑选出食材,使自己可以烹饪出任意正整数美味值的菜肴,初始时菜肴的美味值为 ,每次加入一种食材,他可以选择让菜肴的美味值上升 aia_iai,也可以选择让菜肴的美味值下降 aia_iai(或许最后会弄出来黑暗料理?)。
作为当世名厨,小 Y 自然知道该怎么挑选食材最佳。可是他并不知道有多少种最佳的挑选食材方案,于是他找到了你来帮忙。
我们使用无序数列(b1,b2,…,bm)(b_1,b_2,\ldots,b_m)(b1,b2,…,bm)来表示从原来的n种食材中挑选出了m种食材,第i种食材编号为bib_ibi的方案。同时你需要注意,和为同一种方案且当i≠ji \not =ji=j时,bi≠bjb_i \not = b_jbi=bj。
最佳的挑选食材方案指,挑选出 种食材(1≤m≤n1\leq m\leq n1≤m≤n),让他们能够组合出任意正整数美味值的菜肴。
例如,当n=2,a1=1,a2=2n=2,a_1=1,a_2=2n=2,a1=1,a2=2时,(1),(1,2)\left( 1\right),\left( 1,2\right)(1),(1,2)都是最佳的挑选食材方案。
答案对 998244353取模。
第一行一个正整数 。 第二行 个正整数 ai(1≤ai≤2 000)a_i(1\le a_i\le2\ 000)ai(1≤ai≤2 000)。
输出一个数表示最佳的挑选食材方案的数量对 998244353取模。
示例1
复制
5 1 2 3 4 5
复制
26
示例2
复制
1 2
复制
0
思路参考于:https://blog.csdn.net/Morning_Glory_JR/article/details/100835716
不得不说这个题确实很厉害,我们发现,因为对于每个ai,我们都可以加上ai或者减去ai,那么,如果我们要凑出任意正整数的集合,就必须凑出1;一旦我们能够凑出1,我们就可以凑出任意正整数的集合。
那么什么情况下可以凑出1呢?
1.如果有两个数a,b,b=a+1,则可以凑出1,因为gcd(a,b)==gcd(a,a+1)==1;
2.对于a的每一个因子k,有gcd(k,b)==1,所以这也是满足条件的。
3.利用上述关系,如果一个集合a1,a2.....an,有gcd(a1,a2,....an)==1,那么这个集合是可以构成任意整数的。
所以我们的问题就转变成,在一个集合中选择多少个数,让他们的gcd(a1,a2,...an)==1的方案数。
直接背包就可以了,f(i,j)表示前i个中选择的数的集合,gcd(a1,a2....ak)==j的方案数。
#include
using namespace std;
const int maxn=3000+10;
const int maxm=2000+10;
const int mod=998244353;
int f[maxn][maxm];
int a[maxn];
signed main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
f[1][a[1]]=1;
for(int i=2;i<=n;i++){
f[i][a[i]]=1;
for(int j=1;j<=2000;j++){
f[i][j]=(f[i][j]+f[i-1][j])%mod;
}
for(int j=1;j<=2000;j++){
f[i][__gcd(a[i],j)]=(f[i-1][j]+f[i][__gcd(a[i],j)])%mod;
}
}
printf("%d\n",f[n][1]);
return 0;
}