牛客练习赛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 上山拜师学艺,经过 img年之长的厨艺练习,已成为当世名厨,今天他接受邀请,在众人面前展示自己高超的厨艺。

人们给小 Y 提供了 img种食物,每种食物无限量供应,每种食物都有一个美味值,记为 aia_iai​。

小 Y 为了展示他的厨艺,他需要挑选出食材,使自己可以烹饪出任意正整数美味值的菜肴,初始时菜肴的美味值为 img每次加入一种食材,他可以选择让菜肴的美味值上升 aia_iai​,也可以选择让菜肴的美味值下降 aia_iai​(或许最后会弄出来黑暗料理?)。

作为当世名厨,小 Y 自然知道该怎么挑选食材最佳。可是他并不知道有多少种最佳的挑选食材方案,于是他找到了你来帮忙。

我们使用无序数列(b1,b2,…,bm)(b_1,b_2,\ldots,b_m)(b1,b2,…,bm)来表示从原来的n种食材中挑选出了m种食材,第i种食材编号为bib_ibi的方案。同时你需要注意,imgimg为同一种方案且当i≠ji \not =ji=j时,bi≠bjb_i \not = b_jbi=bj。

最佳的挑选食材方案指,挑选出 img食材(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)都是最佳的挑选食材方案

答案对 img取模。

输入描述:

第一行一个正整数 。第二行 个正整数 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';
        }
    }
}




posted @ 2019-10-08 00:23  茄子Min  阅读(320)  评论(0编辑  收藏  举报