zoukankan      html  css  js  c++  java
  • AtCoder Regular Contest 106 DEF

    ARC106

    D - Powers

    用二项式定理展开,把同类项合并到一起,差不多就是这样:

    把两个括号内分别计算再相乘

    #include <bits/stdc++.h>
    using namespace std;
    #define int long long
    void read (int &x) {
        char ch = getchar(); x = 0; while (!isdigit(ch)) ch = getchar();
        while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
    } const int N = 2e5 + 5, mod = 998244353;
    int n, m, a[N], b[N], pw[350], in[350], sum[305];
    int qpow (int x, int y) {
        int t = 1;
        while (y) {
            if (y & 1) t = t * x % mod;
            x = x * x % mod, y >>= 1;
        } return t;
    }
    signed main() {
        read (n), read (m); pw[0] = in[0] = 1;
        for (int i = 1; i <= n; ++i) read (a[i]);
        for (int i = 1; i <= m; ++i)
            pw[i] = pw[i - 1] * i % mod, in[i] = qpow (pw[i], mod - 2);
        for (int i = 1; i <= n; ++i)
            for (int j = 0, t = 1; j <= m; ++j, t = t * a[i] % mod)
                sum[j] = (sum[j] + t * in[j]) % mod;
        for (int i = 1; i <= n; ++i) a[i] += a[i], b[i] = a[i];
        int inv = qpow (2, mod - 2);
        for (int i = 1; i <= m; ++i) {
            int res = 0;
            for (int j = 0; j <= i; ++j)
                res = (res + sum[j] * sum[i - j]) % mod;
            res *= pw[i]; for (int j = 1; j <= n; ++j) res -= a[j]; res = res % mod + mod;
            printf ("%lld
    ", res * inv % mod);
            for (int j = 1; j <= n; ++j) a[j] = a[j] * b[j] % mod;
        }
        return 0;
    }
    
    

    E - Medals

    容易想到二分答案,可以得出答案上界是 (2 imes n imes k)

    判定可行的方式比较奇怪,用到了 Hall定理 ,把每个人得到每块奖牌看作二分图的左部点,共 (n imes k) 个,把每一天看作右部点。如果第 (i) 个人在第 (j) 天上班,把 (i)(k) 个点都和 (j) 连一条边

    但边数很多,不能直接搞。根据 (hall) 定理,左部点的每个子集 (A) 连向的右部点子集的大小 (P(A)),要满足 (| A|leq|P(A)|)。先考虑同一个人 (i)(k) 个点,这些点连向的点集 (S(i))相同,只要判断一下 (kleq |S(i)|),然后可以把 (k) 个点看成一个

    不同的人组成的点集,共有 (2^n) 种情况。把每一天哪些人来上班用二进制表示。如果某一天的上班情况用二进制表示为 (Q)(Q_i) 表示二进制下第 (i) 位(第 (i) 个人上班否)。(Q) 对哪些组合有贡献呢?显然是二进制表示下和 (Q) 有重合的点集。但这个似乎不好算,可以反过来,把所有点集 (+1),然后没有重合的 (-1),用 (fwt) 计算 (-1) 的过程

    #include <bits/stdc++.h>
    using namespace std;
    #define int long long
    void read (int &x) {
        char ch = getchar(); x = 0; while (!isdigit(ch)) ch = getchar();
        while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
    } const int N = 18, M = 1e5 + 5;
    int n, k, res, d[N], a[1 << N], c[1 << N], p[2 * N * M];
    int check (int t) {
        memset (a, 0, sizeof (a));
        for (int i = 1; i <= t; ++i) ++a[p[i]];
        int mx = 1 << n;
        for (int i = 1; i < mx; i <<= 1)
            for (int j = 0; j < mx; j += (i << 1))
                for (int k = 0; k < i; ++k) a[j | k] += a[j | k | i];
        for (int i = mx - 1; i >= 1; --i)
            if (c[i] * k > t - a[i]) return 0; return 1;
    }
    signed main() {
        read (n), read (k);
        for (int i = 1; i <= n; ++i) read (d[i]);
        c[0] = 0;
        for (int i = 1; i < (1 << n); ++i)
            c[i] = c[i >> 1] + (i & 1);
        for (int i = 1, qwq; i <= 2 * n * k; ++i) {
            p[i] = (1 << n) - 1;
            for (int j = 1; j <= n; ++j) {
                qwq = i % (d[j] << 1);
                if (qwq && qwq <= d[j]) p[i] ^= (1 << j - 1);
            }
        }
        int l = 1, r = 2 * n * k, res = r;
        while (l <= r) {
            int mid = l + r >> 1;
            if (check (mid)) res = mid, r = mid - 1;
            else l = mid + 1;
        }
        return printf ("%lld
    ", res), 0;
    }
    

    F - Figures

    思路巧妙但容易理解的计数问题。

    首先,要保证图连通,不难发现,充要条件是每个 (Part) 都至少使用一个接口。先把这些接口都选了,这是答案的第一部分

    然后,一共使用 (2n-2) 个接口,还剩 (n-2) 个要选,奇妙的事情发生了

    把剩下的所有接口放在一起,假设一共 (S) 个,从中选 (n-2) 个的排列数就是答案的第二部分。(But...Why???)

    其实就是要证明一个排列和一种接法一一对应(这里的排列包括第一部分从每个 (Part) 选的一个接口)对应方法:从左向右扫描整个排列,如果当前接口已经标记过,跳过;否则,从右边选一个最近的在不同 Part 上的接口作为边的另一端。这样就和接法对应上了,并且可以发现改变排列的顺序也会改变连边方案。

    #include <bits/stdc++.h>
    using namespace std;
    #define int long long
    void read (int &x) {
        char ch = getchar(); x = 0; while (!isdigit(ch)) ch = getchar();
        while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
    } const int N = 2e5 + 5, mod = 998244353;
    int n, s, res = 1;
    signed main() {
        read (n);
        for (int i = 1, x; i <= n; ++i)
            read (x), res = res * x % mod, s += x - 1;
        s %= mod;
        for (int i = 1; i <= n - 2; ++i)
            res = res * (s - i + 1) % mod;
        return printf ("%lld
    ", res), 0;
    }
    
  • 相关阅读:
    bzoj-2748 2748: [HAOI2012]音量调节(dp)
    bzoj-2338 2338: [HNOI2011]数矩形(计算几何)
    bzoj-3444 3444: 最后的晚餐(组合数学)
    codeforces 709E E. Centroids(树形dp)
    codeforces 709D D. Recover the String(构造)
    codeforces 709C C. Letters Cyclic Shift(贪心)
    codeforces 709B B. Checkpoints(水题)
    codeforces 709A A. Juicer(水题)
    Repeat Number
    hdu 1003 Max Sum (动态规划)
  • 原文地址:https://www.cnblogs.com/whx666/p/14279273.html
Copyright © 2011-2022 走看看