AtCoder Beginner Contest 352

~Lanly~發表於2024-05-05

A - AtCoder Line (abc352 A)

題目大意

給定\(x, y,z\),問 \(z\)是否在\(x,y\)之間。

解題思路

如果\(x > y\),則交換 \(x,y\),然後判斷是否有 \(x \leq z \leq y\)即可。

神奇的程式碼
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n, x, y, z;
    cin >> n >> x >> y >> z;
    if (x > y)
        swap(x, y);
    if (x <= z && z <= y)
        cout << "Yes" << '\n';
    else
        cout << "No" << '\n';

    return 0;
}



B - Typing (abc352 B)

題目大意

給定字串\(s,t\)\(s\)\(t\)的子序列。現就近匹配,找到 \(s\)的每個字元就近匹配到的 \(t\)中的每個下標。

解題思路

按照就近匹配原則,匹配一次子序列即可。

神奇的程式碼
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    string s, t;
    cin >> s >> t;
    auto pos = t.begin();
    for (auto c : s) {
        while (pos != t.end() && *pos != c) {
            pos = next(pos);
        }
        cout << (pos - t.begin() + 1) << ' ';
        pos = next(pos);
    }
    cout << '\n';

    return 0;
}



C - Standing On The Shoulders (abc352 C)

題目大意

給定\(n\)個巨人,巨人有肩高\(a_i\)和頭高\(b_i\)

現依次放置每個巨人,每個巨人放在上一個巨人的肩上。

問最高的巨人的頭高有多高。

解題思路

列舉最後的巨人是哪個,那麼其頭高就是\(b_i + \sum_{j \neq i} a_j\)

所有可能的情況取最大值即為答案。

神奇的程式碼
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n;
    cin >> n;
    vector<int> a(n), b(n);
    for (int i = 0; i < n; i++)
        cin >> a[i] >> b[i];
    LL suma = accumulate(a.begin(), a.end(), 0LL);
    LL ans = 0;
    for (int i = 0; i < n; i++) {
        ans = max(suma - a[i] + b[i], ans);
    }
    cout << ans << '\n';

    return 0;
}



D - Permutation Subsequence (abc352 D)

題目大意

給定一個排列\(p_i\)和一個數\(k\)

找出\(k\)個下標,最小化下標極差,使得對應的\(p_i\)組成的集合是一個連續的 \(k\)個數的集合。

解題思路

\(pos[i]\)表示數 \(i\)在原排列的位置。列舉這連續的\(k\)個數,比如是\(i,i+1,i+2,...,i+k-1\),那此時的極差就是 \(\max_{i \leq j \leq i+k-1}(pos[j]) - \min_{i \leq j \leq i+k-1}(pos[j])\)

對所有的\(i\)取最小值,就是答案。

按順序列舉\(i\),此時就是求一個長度為 \(k\)\(pos\)區間的最大值和最小值,即經典的滑動視窗問題,用單調維護即可。

神奇的程式碼
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n, k;
    cin >> n >> k;
    vector<int> pos(n);
    for (int i = 0; i < n; i++) {
        int x;
        cin >> x;
        --x;
        pos[x] = i;
    }
    int ans = n + 1;
    deque<int> minn, maxx;
    for (int i = 0; i < n; i++) {
        while (!minn.empty() && i - minn.front() >= k) {
            minn.pop_front();
        }
        while (!maxx.empty() && i - maxx.front() >= k) {
            maxx.pop_front();
        }
        while (!minn.empty() && pos[i] <= pos[minn.back()]) {
            minn.pop_back();
        }
        while (!maxx.empty() && pos[i] >= pos[maxx.back()]) {
            maxx.pop_back();
        }
        minn.push_back(i);
        maxx.push_back(i);
        if (i >= k - 1)
            ans = min(ans, pos[maxx.front()] - pos[minn.front()]);
    }
    cout << ans << '\n';

    return 0;
}



E - Clique Connect (abc352 E)

題目大意

給定一張無向圖,初始無邊。

現依次進行\(q\)次操作,每次操作給定 \(k_i\)個點 \(x_{i,j}\)和邊權 \(c_i\),將這 \(k_i\)個點倆倆 連邊,邊權為\(c\)

問最後圖是否連通,若連通,問最小生成樹的權值。

解題思路

用並查集維護連通性,對於每次操作,就依次合併相鄰兩點\(x_{i,j},x_{i,j+1}\)即可。那麼總的複雜度是\(O(\sum k_i)\)。考慮如何求最小生成樹。

參考\(kruskal\)演算法,直接從邊權最小的開始連邊,連邊就\(x_{i,j},x_{i,j+1}\)相連即可,如果它們未相連的話。這個過程可以在並查集合並時順便做了。

因此時間複雜度就是\(O(\sum k_i)\)

神奇的程式碼
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

class dsu {
  public:
    vector<int> p;
    vector<int> sz;
    int n;

    dsu(int _n) : n(_n) {
        p.resize(n);
        sz.resize(n);
        iota(p.begin(), p.end(), 0);
        fill(sz.begin(), sz.end(), 1);
    }

    inline int get(int x) { return (x == p[x] ? x : (p[x] = get(p[x]))); }

    inline bool unite(int x, int y) {
        x = get(x);
        y = get(y);
        if (x != y) {
            p[x] = y;
            sz[y] += sz[x];
            return true;
        }
        return false;
    }
};

int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n, m;
    cin >> n >> m;
    vector<pair<vector<int>, int>> a(m);
    for (auto& [p, c] : a) {
        int k;
        cin >> k >> c;
        p.resize(k);
        for (auto& i : p) {
            cin >> i;
            --i;
        }
    }
    vector<int> id(m);
    iota(id.begin(), id.end(), 0);
    sort(id.begin(), id.end(),
         [&](int i, int j) { return a[i].second < a[j].second; });
    dsu d(n);
    LL ans = 0;
    for (auto& i : id) {
        auto& [p, c] = a[i];
        for (auto j : p) {
            if (d.unite(p[0], j)) {
                ans += c;
            }
        }
    }
    if (d.sz[d.get(0)] != n) {
        ans = -1;
    }
    cout << ans << '\n';

    return 0;
}



F - Estimate Order (abc352 F)

題目大意

\(n\)個人,排名唯一,給定關於這些人的\(m\)條排名資訊,問每個人的名次是否能唯一確定。

資訊為\(i,j,c_k\),表示第\(i\)個人的名次 \(r_i\)與第 \(j\)個人的名字 \(r_j\) 的差\(r_i - r_j = c_k\)

解題思路

考慮樸素做法怎麼做。

\(n\)個人,每條資訊就給兩個人連邊,然後就得到有若干個連通塊。

對於每個連通塊,如果我指定其中一個人的排名,那該連通塊的其他人的排名都唯一確定了。

樸素的做法就是依次指定每個連通塊中任意一個人的排名,然後求得該連通塊其他人的排名,沒有衝突的話,就繼續指定下一個連通塊的任意一個人的排名。如果存在合法的排名,則記錄下來。最後看每個連通塊合法的排名的個數是否是 \(1\),是則唯一確定,否則不唯一。

樸素做法的時間複雜度\(O(n!)\) ,這裡可以加點最佳化,比如只有一個點的我們不考慮,那麼最多隻有\(\frac{n}{2}\)個連通塊,則此時的複雜度就是 \(A_{n}^{\frac{n}{2}}\),有 \(5e8\)

考慮最佳化,在搜尋時我們保留了每個數字用在了哪個連通塊上,因此其狀態數是一個階乘,但事實上這個狀態是冗餘的——我們只需要知道用了哪些數字,而不需要這些數字用在了哪些連通塊上。

考慮轉移,對當前連通塊,我們需要列舉一個沒用過的數字,然後求該連通塊每個人的排名,看看排名是否唯一。也就是說,我們需要的狀態僅僅只是數字的是否使用,就可以做到轉移,而不需要知道數字用在了哪裡,由此狀態數可以從階乘級別降到指數級別。

然後,再記憶化搜尋一下就解決了。

即設\(dp[s]\)表示當前使用的數字狀態是\(s\), 剩餘連通塊填剩餘數字,是否存在可行解。轉移就列舉該連通塊的一個數字,然後\(BFS\)下得到整個連通塊需要用到的數字\(nxt\),沒衝突的話,如果\(dp[s | nxt]\)可行,則說明當前連通塊填該數字是合法的,記錄一下。

最後看每個連通塊的合法數字個數是否唯一,是則能唯一確定。

這個\(dp\)的設計方式其實就是樸素的記憶化搜尋,跟遞迴形式的 數位 \(dp\)很類似。

神奇的程式碼
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n, m;
    cin >> n >> m;
    vector<vector<array<int, 2>>> edge(n);
    for (int i = 0; i < m; i++) {
        int a, b, c;
        cin >> a >> b >> c;
        --a, --b;
        edge[a].push_back({b, -c});
        edge[b].push_back({a, c});
    }
    int up = (1 << n);
    vector<int> used(n, 0);
    vector<int> st;
    auto bfs1 = [&](int st) {
        queue<int> team;
        team.push(st);
        used[st] = 1;
        while (!team.empty()) {
            int u = team.front();
            team.pop();
            for (auto& [v, c] : edge[u]) {
                if (!used[v]) {
                    used[v] = 1;
                    team.push(v);
                }
            }
        }
    };
    for (int i = 0; i < n; ++i) {
        if (used[i]) {
            continue;
        }
        st.push_back(i);
        bfs1(i);
    }

    vector<int> dp(up, -1);
    vector<set<int>> candidate(st.size());
    dp.back() = 1;

    auto bfs2 = [&](int st, int sc) -> array<int, 2> {
        int ret = 0;
        queue<array<int, 2>> team;
        vector<int> val(n, -1);
        team.push({st, sc});
        val[st] = sc;
        ret |= (1 << sc);
        int sz = 0;
        while (!team.empty()) {
            auto [u, c] = team.front();
            team.pop();
            sz++;
            for (auto& [v, w] : edge[u]) {
                int nc = c + w;
                if (val[v] == -1) {
                    if (nc < 0 || nc >= n || (ret >> nc) & 1) {
                        return {false, 0};
                    }

                    ret |= (1 << nc);
                    val[v] = nc;
                    team.push({v, nc});
                } else if (val[v] != nc) {
                    return {false, 0};
                }
            }
        }
        assert(sz == __builtin_popcount(ret));
        return {true, ret};
    };
    auto dfs = [&](auto&& dfs, int state, int pos) -> int {
        if (dp[state] != -1)
            return dp[state];
        bool ok = false;
        int start = st[pos];
        for (int i = 0; i < n; ++i) {
            auto [valid, nxt] = bfs2(start, i);
            if (valid && ((state & nxt) == 0) &&
                dfs(dfs, state | nxt, pos + 1)) {
                ok = true;
                candidate[pos].insert(i);
            }
        }
        return dp[state] = ok;
    };
    dfs(dfs, 0, 0);

    vector<int> ans(n, -2);
    auto bfs3 = [&](int st, int w) {
        queue<array<int, 2>> team;
        team.push({st, w});
        ans[st] = w;
        while (!team.empty()) {
            auto [u, c] = team.front();
            team.pop();
            for (auto& [v, w] : edge[u]) {
                int nc = c + w;
                if (ans[v] == -2) {
                    ans[v] = nc;
                    team.push({v, nc});
                }
            }
        }
    };
    for (int i = 0; i < st.size(); ++i) {
        if (candidate[i].size() == 1) {
            bfs3(st[i], *candidate[i].begin());
        }
    }
    for (int i = 0; i < n; ++i) {
        cout << ans[i] + 1 << " \n"[i == n - 1];
    }

    return 0;
}


G - Socks 3 (abc352 G)

題目大意

抽屜裡有\(n\)個顏色的襪子,各有 \(a_i\)只。

現每次從抽屜裡等機率取一隻襪子,不放回,直到取出兩隻顏色相同的襪子為止。

問取出的襪子的期望只數。

解題思路

假設恰好取出\(i\)只襪子後,有兩隻顏色相同的機率是 \(q_i\),那麼期望次數即為 \(\sum i q_i\)。(這個式子比較經典,是一個字尾和的字尾和的展開式。)

假設取出\(i\)只或以上後,有兩隻顏色相同的機率是 \(p_i\),即 \(p_i = \sum_{j \geq i} q_i\),是個字尾和,則 \(q_i = p_{i} - p_{i+1}\)。代替上述期望式子得 \(\sum p_i\)

\(p_{i+1}\)表示需要取超過\(i\)只襪子才能有兩隻襪子顏色相同的機率,換句話說,它就是取\(i\)只襪子,沒有兩隻顏色相同的機率(這才需要取\(i+1\)只或以上才能有兩隻襪子顏色相同,它不意味著第 \(i+1\)只能取到顏色相同)。

而取\(i\)只顏色各不相同的機率,可先列舉對應的 \(i\)種顏色,然後\(\frac{\text{對應數量相乘}}{\text{總情況數}}\)。從生成函式的角度考慮,每種顏色的生成函式是 \(a_i x + 1\),則所有顏色的生成函式相乘 \((a_1x+1)(a_2x+1)...(a_nx+1)\)的結果的 \(x^i\)的係數就是上述的分子,分母(總情況數)就是 \(C_{tot}^k\)\(tot = \sum a_i\)

因此需要求出 \((a_1x+1)(a_2x+1)...(a_nx+1)\)的結果,樸素的乘法需要進行\(O(n)\)次,每次用 \(ntt\)最佳化的話,總複雜度也得是 \(O(n^2 \log n)\)

因為每次相乘,多項式的最高冪才 \(+1\),可以考慮分治,假設\(f(l,r) = \prod_{l \leq i \leq r} (a_ix + 1)\),要求\(f(1,n)\),則先求 \(f(1, \frac{n}{2})\)\(f(\frac{n}{2} + 1, n)\),再兩者相乘,就得到 \(f(1,n)\),而 \(f(1, \frac{n}{2})\)\(f(\frac{n}{2} + 1, n)\)就遞迴求解。這樣每次相乘,多項式的最高冪就會翻倍,最多翻\(\log\)次就得到結果,而不是之前的\(n\)次。即總的時間複雜度是 \(O(n \log^2 n)\)

程式碼是貼了\(tourist\)\(ntt\)板子和取模板子。

(感覺轉換部分不能一眼看透,上面說的感覺不像是本質

神奇的程式碼
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

template <typename T> T inverse(T a, T m) {
    T u = 0, v = 1;
    while (a != 0) {
        T t = m / a;
        m -= t * a;
        swap(a, m);
        u -= t * v;
        swap(u, v);
    }
    assert(m == 1);
    return u;
}

template <typename T> class Modular {
  public:
    using Type = typename decay<decltype(T::value)>::type;

    constexpr Modular() : value() {}
    template <typename U> Modular(const U& x) { value = normalize(x); }

    template <typename U> static Type normalize(const U& x) {
        Type v;
        if (-mod() <= x && x < mod())
            v = static_cast<Type>(x);
        else
            v = static_cast<Type>(x % mod());
        if (v < 0)
            v += mod();
        return v;
    }

    const Type& operator()() const { return value; }
    template <typename U> explicit operator U() const {
        return static_cast<U>(value);
    }
    constexpr static Type mod() { return T::value; }

    Modular& operator+=(const Modular& other) {
        if ((value += other.value) >= mod())
            value -= mod();
        return *this;
    }
    Modular& operator-=(const Modular& other) {
        if ((value -= other.value) < 0)
            value += mod();
        return *this;
    }
    template <typename U> Modular& operator+=(const U& other) {
        return *this += Modular(other);
    }
    template <typename U> Modular& operator-=(const U& other) {
        return *this -= Modular(other);
    }
    Modular& operator++() { return *this += 1; }
    Modular& operator--() { return *this -= 1; }
    Modular operator++(int) {
        Modular result(*this);
        *this += 1;
        return result;
    }
    Modular operator--(int) {
        Modular result(*this);
        *this -= 1;
        return result;
    }
    Modular operator-() const { return Modular(-value); }

    template <typename U = T>
    typename enable_if<is_same<typename Modular<U>::Type, int>::value,
                       Modular>::type&
    operator*=(const Modular& rhs) {
#ifdef _WIN32
        uint64_t x =
            static_cast<int64_t>(value) * static_cast<int64_t>(rhs.value);
        uint32_t xh = static_cast<uint32_t>(x >> 32),
                 xl = static_cast<uint32_t>(x), d, m;
        asm("divl %4; \n\t" : "=a"(d), "=d"(m) : "d"(xh), "a"(xl), "r"(mod()));
        value = m;
#else
        value = normalize(static_cast<int64_t>(value) *
                          static_cast<int64_t>(rhs.value));
#endif
        return *this;
    }
    template <typename U = T>
    typename enable_if<is_same<typename Modular<U>::Type, int64_t>::value,
                       Modular>::type&
    operator*=(const Modular& rhs) {
        int64_t q = static_cast<int64_t>(static_cast<long double>(value) *
                                         rhs.value / mod());
        value = normalize(value * rhs.value - q * mod());
        return *this;
    }
    template <typename U = T>
    typename enable_if<!is_integral<typename Modular<U>::Type>::value,
                       Modular>::type&
    operator*=(const Modular& rhs) {
        value = normalize(value * rhs.value);
        return *this;
    }

    Modular& operator/=(const Modular& other) {
        return *this *= Modular(inverse(other.value, mod()));
    }

    template <typename U>
    friend bool operator==(const Modular<U>& lhs, const Modular<U>& rhs);

    template <typename U>
    friend bool operator<(const Modular<U>& lhs, const Modular<U>& rhs);

    template <typename U>
    friend std::istream& operator>>(std::istream& stream, Modular<U>& number);

  private:
    Type value;
};

template <typename T>
bool operator==(const Modular<T>& lhs, const Modular<T>& rhs) {
    return lhs.value == rhs.value;
}
template <typename T, typename U>
bool operator==(const Modular<T>& lhs, U rhs) {
    return lhs == Modular<T>(rhs);
}
template <typename T, typename U>
bool operator==(U lhs, const Modular<T>& rhs) {
    return Modular<T>(lhs) == rhs;
}

template <typename T>
bool operator!=(const Modular<T>& lhs, const Modular<T>& rhs) {
    return !(lhs == rhs);
}
template <typename T, typename U>
bool operator!=(const Modular<T>& lhs, U rhs) {
    return !(lhs == rhs);
}
template <typename T, typename U>
bool operator!=(U lhs, const Modular<T>& rhs) {
    return !(lhs == rhs);
}

template <typename T>
bool operator<(const Modular<T>& lhs, const Modular<T>& rhs) {
    return lhs.value < rhs.value;
}

template <typename T>
Modular<T> operator+(const Modular<T>& lhs, const Modular<T>& rhs) {
    return Modular<T>(lhs) += rhs;
}
template <typename T, typename U>
Modular<T> operator+(const Modular<T>& lhs, U rhs) {
    return Modular<T>(lhs) += rhs;
}
template <typename T, typename U>
Modular<T> operator+(U lhs, const Modular<T>& rhs) {
    return Modular<T>(lhs) += rhs;
}

template <typename T>
Modular<T> operator-(const Modular<T>& lhs, const Modular<T>& rhs) {
    return Modular<T>(lhs) -= rhs;
}
template <typename T, typename U>
Modular<T> operator-(const Modular<T>& lhs, U rhs) {
    return Modular<T>(lhs) -= rhs;
}
template <typename T, typename U>
Modular<T> operator-(U lhs, const Modular<T>& rhs) {
    return Modular<T>(lhs) -= rhs;
}

template <typename T>
Modular<T> operator*(const Modular<T>& lhs, const Modular<T>& rhs) {
    return Modular<T>(lhs) *= rhs;
}
template <typename T, typename U>
Modular<T> operator*(const Modular<T>& lhs, U rhs) {
    return Modular<T>(lhs) *= rhs;
}
template <typename T, typename U>
Modular<T> operator*(U lhs, const Modular<T>& rhs) {
    return Modular<T>(lhs) *= rhs;
}

template <typename T>
Modular<T> operator/(const Modular<T>& lhs, const Modular<T>& rhs) {
    return Modular<T>(lhs) /= rhs;
}
template <typename T, typename U>
Modular<T> operator/(const Modular<T>& lhs, U rhs) {
    return Modular<T>(lhs) /= rhs;
}
template <typename T, typename U>
Modular<T> operator/(U lhs, const Modular<T>& rhs) {
    return Modular<T>(lhs) /= rhs;
}

template <typename T, typename U>
Modular<T> power(const Modular<T>& a, const U& b) {
    assert(b >= 0);
    Modular<T> x = a, res = 1;
    U p = b;
    while (p > 0) {
        if (p & 1)
            res *= x;
        x *= x;
        p >>= 1;
    }
    return res;
}

template <typename T> string to_string(const Modular<T>& number) {
    return to_string(number());
}

template <typename T>
std::ostream& operator<<(std::ostream& stream, const Modular<T>& number) {
    return stream << number();
}

template <typename T>
std::istream& operator>>(std::istream& stream, Modular<T>& number) {
    typename common_type<typename Modular<T>::Type, int64_t>::type x;
    stream >> x;
    number.value = Modular<T>::normalize(x);
    return stream;
}

constexpr int md = 998244353;
using Mint = Modular<std::integral_constant<decay<decltype(md)>::type, md>>;

template <typename T> class NTT {
  public:
    using Type = typename decay<decltype(T::value)>::type;

    static Type md;
    static Modular<T> root;
    static int base;
    static int max_base;
    static vector<Modular<T>> roots;
    static vector<int> rev;

    static void clear() {
        root = 0;
        base = 0;
        max_base = 0;
        roots.clear();
        rev.clear();
    }

    static void init() {
        md = T::value;
        assert(md >= 3 && md % 2 == 1);
        auto tmp = md - 1;
        max_base = 0;
        while (tmp % 2 == 0) {
            tmp /= 2;
            max_base++;
        }
        root = 2;
        while (power(root, (md - 1) >> 1) == 1) {
            root++;
        }
        assert(power(root, md - 1) == 1);
        root = power(root, (md - 1) >> max_base);
        base = 1;
        rev = {0, 1};
        roots = {0, 1};
    }

    static void ensure_base(int nbase) {
        if (md != T::value) {
            clear();
        }
        if (roots.empty()) {
            init();
        }
        if (nbase <= base) {
            return;
        }
        assert(nbase <= max_base);
        rev.resize(1 << nbase);
        for (int i = 0; i < (1 << nbase); i++) {
            rev[i] = (rev[i >> 1] >> 1) + ((i & 1) << (nbase - 1));
        }
        roots.resize(1 << nbase);
        while (base < nbase) {
            Modular<T> z = power(root, 1 << (max_base - 1 - base));
            for (int i = 1 << (base - 1); i < (1 << base); i++) {
                roots[i << 1] = roots[i];
                roots[(i << 1) + 1] = roots[i] * z;
            }
            base++;
        }
    }

    static void fft(vector<Modular<T>>& a) {
        int n = (int)a.size();
        assert((n & (n - 1)) == 0);
        int zeros = __builtin_ctz(n);
        ensure_base(zeros);
        int shift = base - zeros;
        for (int i = 0; i < n; i++) {
            if (i < (rev[i] >> shift)) {
                swap(a[i], a[rev[i] >> shift]);
            }
        }
        for (int k = 1; k < n; k <<= 1) {
            for (int i = 0; i < n; i += 2 * k) {
                for (int j = 0; j < k; j++) {
                    Modular<T> x = a[i + j];
                    Modular<T> y = a[i + j + k] * roots[j + k];
                    a[i + j] = x + y;
                    a[i + j + k] = x - y;
                }
            }
        }
    }

    static vector<Modular<T>> multiply(vector<Modular<T>> a,
                                       vector<Modular<T>> b) {
        if (a.empty() || b.empty()) {
            return {};
        }
        int eq = (a == b);
        int need = (int)a.size() + (int)b.size() - 1;
        int nbase = 0;
        while ((1 << nbase) < need)
            nbase++;
        ensure_base(nbase);
        int sz = 1 << nbase;
        a.resize(sz);
        b.resize(sz);
        fft(a);
        if (eq)
            b = a;
        else
            fft(b);
        Modular<T> inv_sz = 1 / static_cast<Modular<T>>(sz);
        for (int i = 0; i < sz; i++) {
            a[i] *= b[i] * inv_sz;
        }
        reverse(a.begin() + 1, a.end());
        fft(a);
        a.resize(need);
        return a;
    }
};

template <typename T> typename NTT<T>::Type NTT<T>::md;
template <typename T> Modular<T> NTT<T>::root;
template <typename T> int NTT<T>::base;
template <typename T> int NTT<T>::max_base;
template <typename T> vector<Modular<T>> NTT<T>::roots;
template <typename T> vector<int> NTT<T>::rev;

template <typename T> vector<Modular<T>> inverse(const vector<Modular<T>>& a) {
    assert(!a.empty());
    int n = (int)a.size();
    vector<Modular<T>> b = {1 / a[0]};
    while ((int)b.size() < n) {
        vector<Modular<T>> x(a.begin(),
                             a.begin() + min(a.size(), b.size() << 1));
        x.resize(b.size() << 1);
        b.resize(b.size() << 1);
        vector<Modular<T>> c = b;
        NTT<T>::fft(c);
        NTT<T>::fft(x);
        Modular<T> inv = 1 / static_cast<Modular<T>>((int)x.size());
        for (int i = 0; i < (int)x.size(); i++) {
            x[i] *= c[i] * inv;
        }
        reverse(x.begin() + 1, x.end());
        NTT<T>::fft(x);
        rotate(x.begin(), x.begin() + (x.size() >> 1), x.end());
        fill(x.begin() + (x.size() >> 1), x.end(), 0);
        NTT<T>::fft(x);
        for (int i = 0; i < (int)x.size(); i++) {
            x[i] *= c[i] * inv;
        }
        reverse(x.begin() + 1, x.end());
        NTT<T>::fft(x);
        for (int i = 0; i < ((int)x.size() >> 1); i++) {
            b[i + ((int)x.size() >> 1)] = -x[i];
        }
    }
    b.resize(n);
    return b;
}

template <typename T> vector<Modular<T>> inverse_old(vector<Modular<T>> a) {
    assert(!a.empty());
    int n = (int)a.size();
    if (n == 1) {
        return {1 / a[0]};
    }
    int m = (n + 1) >> 1;
    vector<Modular<T>> b =
        inverse(vector<Modular<T>>(a.begin(), a.begin() + m));
    int need = n << 1;
    int nbase = 0;
    while ((1 << nbase) < need) {
        ++nbase;
    }
    NTT<T>::ensure_base(nbase);
    int size = 1 << nbase;
    a.resize(size);
    b.resize(size);
    NTT<T>::fft(a);
    NTT<T>::fft(b);
    Modular<T> inv = 1 / static_cast<Modular<T>>(size);
    for (int i = 0; i < size; ++i) {
        a[i] = (2 - a[i] * b[i]) * b[i] * inv;
    }
    reverse(a.begin() + 1, a.end());
    NTT<T>::fft(a);
    a.resize(n);
    return a;
}

template <typename T>
vector<Modular<T>> operator*(const vector<Modular<T>>& a,
                             const vector<Modular<T>>& b) {
    if (a.empty() || b.empty()) {
        return {};
    }
    if (min(a.size(), b.size()) < 150) {
        vector<Modular<T>> c(a.size() + b.size() - 1, 0);
        for (int i = 0; i < (int)a.size(); i++) {
            for (int j = 0; j < (int)b.size(); j++) {
                c[i + j] += a[i] * b[j];
            }
        }
        return c;
    }
    return NTT<T>::multiply(a, b);
}

template <typename T>
vector<Modular<T>>& operator*=(vector<Modular<T>>& a,
                               const vector<Modular<T>>& b) {
    return a = a * b;
}

int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n;
    cin >> n;
    vector<int> a(n);
    for (auto& x : a)
        cin >> x;
    int tot = accumulate(a.begin(), a.end(), 0);
    auto solve = [&](auto solve, int l, int r) -> vector<Mint> {
        if (l + 1 == r)
            return {1, a[l]};
        int m = (l + r) >> 1;
        return solve(solve, l, m) * solve(solve, m, r);
    };
    vector<Mint> poly = solve(solve, 0, n);
    Mint deno = 1, ans = 0;
    for (int i = 0; i <= n; i++) {
        ans += poly[i] / deno;
        deno *= tot - i;
        deno /= i + 1;
    }
    cout << ans << '\n';

    return 0;
}