牛客练习赛52 | C | [烹饪] (DP,裴蜀定理,gcd)
牛客练习赛52
C | 烹饪 |
---|---|
链接: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)都是最佳的挑选食材方案。
答案对 取模。
输入描述:
第一行一个正整数 。第二行 个正整数 ai(1≤ai≤2 000)a_i(1\le a_i\le2\ 000)ai(1≤ai≤2 000)。
输出描述:
输出一个数表示最佳的挑选食材方案的数量对 取模。
示例1
输入
[复制](javascript:void(0)😉
5
1 2 3 4 5
输出
[复制](javascript:void(0)😉
26
示例2
输入
[复制](javascript:void(0)😉
1
2
输出
[复制](javascript:void(0)😉
0
思路:
有一个结论十分显然,即能组合出[1,n][1,n][1,n]的任意正整数的充要条件时能组成1
根据裴蜀定理ax+by=gcd(a,b),显然这扩展到n元组也成立
于是问题转换成为:选出一个集合,集合中每个数可以选多次,并且这个集合gcd为1,
设f[i]表示组成集合gcd为i的方案数
显然f[gcd(x,j)]=∑f[j]
注意方案多算了一倍,除以二就行了
于是O(nmlogm)解决了
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <vector>
#include <iomanip>
#define ALL(x) (x).begin(), (x).end()
#define sz(a) int(a.size())
#define rep(i,x,n) for(int i=x;i<n;i++)
#define repd(i,x,n) for(int i=x;i<=n;i++)
#define pii pair<int,int>
#define pll pair<long long ,long long>
#define gbtb ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define MS0(X) memset((X), 0, sizeof((X)))
#define MSC0(X) memset((X), '\0', sizeof((X)))
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define eps 1e-6
#define gg(x) getInt(&x)
#define chu(x) cout<<"["<<#x<<" "<<(x)<<"]"<<endl
#define du3(a,b,c) scanf("%d %d %d",&(a),&(b),&(c))
#define du2(a,b) scanf("%d %d",&(a),&(b))
#define du1(a) scanf("%d",&(a));
using namespace std;
typedef long long ll;
ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
ll lcm(ll a, ll b) {return a / gcd(a, b) * b;}
ll powmod(ll a, ll b, ll MOD) {a %= MOD; if (a == 0ll) {return 0ll;} ll ans = 1; while (b) {if (b & 1) {ans = ans * a % MOD;} a = a * a % MOD; b >>= 1;} return ans;}
void Pv(const vector<int> &V) {int Len = sz(V); for (int i = 0; i < Len; ++i) {printf("%d", V[i] ); if (i != Len - 1) {printf(" ");} else {printf("\n");}}}
void Pvl(const vector<ll> &V) {int Len = sz(V); for (int i = 0; i < Len; ++i) {printf("%lld", V[i] ); if (i != Len - 1) {printf(" ");} else {printf("\n");}}}
inline void getInt(int* p);
const int maxn = 1000010;
const int inf = 0x3f3f3f3f;
/*** TEMPLATE CODE * * STARTS HERE ***/
const ll mod = 998244353ll;
ll f[maxn];
ll a[maxn];
int n;
int main()
{
//freopen("D:\\code\\text\\input.txt","r",stdin);
//freopen("D:\\code\\text\\output.txt","w",stdout);
gbtb;
cin >> n;
repd(i, 1, n)
{
cin >> a[i];
}
repd(i, 1, n)
{
f[a[i]]++;
repd(j, 1, 2000)
{
ll x = gcd(a[i], j);
f[x] += f[j];
f[x] %= mod;
}
}
cout << f[1]*powmod(2, mod - 2ll, mod) % mod << endl;
return 0;
}
inline void getInt(int* p) {
char ch;
do {
ch = getchar();
} while (ch == ' ' || ch == '\n');
if (ch == '-') {
*p = -(getchar() - '0');
while ((ch = getchar()) >= '0' && ch <= '9') {
*p = *p * 10 - ch + '0';
}
}
else {
*p = ch - '0';
while ((ch = getchar()) >= '0' && ch <= '9') {
*p = *p * 10 + ch - '0';
}
}
}