zoukankan      html  css  js  c++  java
  • 「2019纪中集训Day13」解题报告

    T1、Count

    求有多少有序 (k) 元组满足 (sum_{i = 1}^k a_i = n) 且不存在 (a_i equiv 0 mod m),答案 (mod 998244353)((n le 10^{18}, m leq 5 imes 10 ^3, k leq 2 imes 10 ^ 3))

    (Sol)

    (A = sum_{i = 1}^{k} a_i mod m equiv n mod m),由于 (a_i mod m) 不会超过 (m - 1),所以 (sum_{i = 1} ^ k a_i) 不会超过 (k (m - 1)),那么 (A) 最多有 (k) 中取值,不妨枚举这 (k)(A)。这样一来就有 ((n - A) / m)(m) 可以随机分配,这个可以用隔板法计算;对于 (A) 的分配可以枚举有多少数大于 (m - 1) 来容斥。

    时间复杂度 (O(km))

    (Source)

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    typedef long long ll;
    int in() {
        int x = 0; char c = getchar(); bool f = 0;
        while (c < '0' || c > '9')
            f |= c == '-', c = getchar();
        while (c >= '0' && c <= '9')
            x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
        return f ? -x : x;
    }
    ll lin() {
        ll x = 0; char c = getchar(); bool f = 0;
        while (c < '0' || c > '9')
            f |= c == '-', c = getchar();
        while (c >= '0' && c <= '9')
            x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
        return f ? -x : x;
    }
    template<typename T>inline void chk_min(T &_, T __) { _ = _ < __ ? _ : __; }
    template<typename T>inline void chk_max(T &_, T __) { _ = _ > __ ? _ : __; }
    
    const int N = 1e7 + 5, mod = 998244353;
    
    ll n;
    int m, k, fac[N], inv[N];
    
    int qpow(int base, int b, int ret = 1) {
        for (; b; b >>= 1, base = 1ll * base * base % mod)
            if (b & 1)
                ret = 1ll * ret * base % mod;
        return ret;
    }
    
    void prep() {
        fac[0] = inv[0] = 1;
        for (int i = 1; i < N; ++i)
            fac[i] = 1ll * fac[i - 1] * i % mod;
        inv[N - 1] = qpow(fac[N - 1], mod - 2);
        for (int i = N - 2; i; --i)
            inv[i] = 1ll * inv[i + 1] * (i + 1) % mod;
    }
    
    inline int C(const int n, const int m) {
        if (n < m)
            return 0;
        return 1ll * fac[n] * inv[m] % mod * inv[n - m] % mod;
    }
    
    int calc(int A) {
        int ret = 0, lim = std::min(A / (m - 1), k);
        for (int i = 0; i <= lim; ++i) {
            ret += (i & 1 ? -1ll : 1ll) * C(k, i) * C(A - i * (m - 1) - 1, k - 1) % mod;
            if (ret >= mod)
                ret -= mod;
            if (ret < 0)
                ret += mod;
        }
        return ret;
    }
    
    int work() {
        int ret = 0, tmp;
        for (int A = n % m; A <= n && A <= k * (m - 1); A += m) {
            tmp = inv[k - 1];
            for (ll i = (n - A) / m + k - 1; i >= (n - A) / m + 1; --i)
                tmp = i % mod * tmp % mod;
            ret += 1ll * tmp * calc(A) % mod;
            if (ret >= mod)
                ret -= mod;
        }
        return ret;
    }
    
    int main() {
        //freopen("in", "r", stdin);
        freopen("count.in", "r", stdin);
        freopen("count.out", "w", stdout);
        n = lin(), m = in(), k = in();
        prep();
        printf("%d
    ", work());
        return 0;
    }
    

    T2、普及组

    给定一个数 (x) (数据中已给出),(T (T leq 2 imes 10 ^ 5)) 组询问,每组询问求大小为 (n imes n (n leq 5 imes 10 ^ 6)) 的好矩阵,好矩阵需满足:
    1、矩阵里的数都是整数;
    2、(forall j, prod_{i = 1} ^ n a_{i , j} = x)
    3、(forall j, prod_{i = 1} ^ n a_{j , i} = x)
    答案 (mod 998244353)

    (Sol)

    观察(打表)发现,(x) 的质因子指数不超过 (2)
    因为每个质因子是独立的,单独考虑贡献。
    若质因子指数不超过 (1),则方案数显然为 (n!)
    否则可以将问题转化为:在 (n imes n) 的矩阵中填数字,保证每行每列和都为 (2)
    (f_i , g_i) 表示 (i imes i) 的矩阵中最后一列填了两个 (1) 或一个 (2) 的方案数。
    转移时考虑多加一行一列;
    (g) 的转移是显然的:

    [g_i = (f_{i - 1} + g_{i - 1}) imes i ]

    (f) 的转移可以考虑 (i imes i) 的矩阵中删掉一列一行 (不妨设是最后一列):

    [f_i = inom{i}{2} imes (2 f_{i - 1} + g_{i - 1}) ]

    答案即为 (f_n + g_n)

    时间复杂度 (O(T log_2 n + n))

    (Source)

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    typedef long long ll;
    int in() {
        int x = 0; char c = getchar(); bool f = 0;
        while (c < '0' || c > '9')
            f |= c == '-', c = getchar();
        while (c >= '0' && c <= '9')
            x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
        return f ? -x : x;
    }
    ll lin() {
        ll x = 0; char c = getchar(); bool f = 0;
        while (c < '0' || c > '9')
            f |= c == '-', c = getchar();
        while (c >= '0' && c <= '9')
            x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
        return f ? -x : x;
    }
    template<typename T>inline void chk_min(T &_, T __) { _ = _ < __ ? _ : __; }
    template<typename T>inline void chk_max(T &_, T __) { _ = _ > __ ? _ : __; }
    
    const int N = 5e6 + 5, mod = 998244353;
    
    ll x;
    int T, n, f[N], g[N], fac[N], cnt[3];
    
    int qpow(int base, int b, int ret = 1) {
        for (; b; base = 1ll * base * base % mod, b >>= 1)
            if (b & 1)
                ret = 1ll * ret * base % mod;
        return ret;
    }
    
    void prep() {
        for (ll i = 2; i * i <= x; ++i)
            if (x % i == 0) {
                int tmp = 0;
                while (x % i == 0)
                    x /= i, ++tmp;
                ++cnt[tmp];
            }
        if (x > 1)
            ++cnt[1];
        g[1] = fac[0] = fac[1] = 1;
        for (int i = 2; i < N; ++i) {
            fac[i] = 1ll * fac[i - 1] * i % mod;
            g[i] = 1ll * (g[i - 1] + f[i - 1]) * i % mod;
            f[i] = 1ll * (1ll * f[i - 1] + f[i - 1] + g[i - 1]) * i % mod * (i - 1) % mod * 499122177ll % mod;
        }
    }
    
    int main() {
        //freopen("in", "r", stdin);
        freopen("pj.in", "r", stdin);
        freopen("pj.out", "w", stdout);
        x = lin(), T = in();
        prep();
        while (T--) {
            n = in();
            printf("%lld
    ", 1ll * qpow(f[n] + g[n], cnt[2]) * qpow(fac[n], cnt[1]) % mod * qpow(2, 1ll * (n - 1) * (n - 1) % (mod - 1)) % mod);
        }
        return 0;
    }
    

    T3、提高组

    (1) ~ (n (n leq 10 ^ 7))的排列中,确定 (a_x = y) 后最长下降子序列不超过 (2) 的方案数,(T (T leq 10 ^ 6)) 组询问,答案对 (10 ^ 9 + 7) 取余。

    (Sol)

    这个排列的前缀 (max) 序列一定与满足条件的排列一一对应,证明:
    考虑对前缀 (max) 中发生变化的位置,这些位置上的数一定是递增的,对于其中一个位置上的数,在它之后出现比他小的数一定也是单调递增的,所以有:
    1、一个排列一定对应它的前缀 (max) 序列;
    2、排列中没有对前缀 (max) 序列造成影响的数也是确定的。
    (QED)

    分类讨论:
    (y geq x),那么 (y) 一定对 (max) 造成影响,可以考虑反证:
    假设 (y) 未造成影响,则在它之前一定有一个大于它的数;
    为了满足题意,小于 (y) 的数一定在它之前,由于 (y - 1 > x - 2),所以假设不成立。

    (y < x),显然 (y) 不对 (max) 造成影响;
    可以考虑排列的逆转置 (A^{-1}[y] = x),还是只用考虑 (y geq x) 的情况。

    整理一下就是求:
    1、(max_i geq i)
    2、(max_i geq max_{i - 1})
    3、(max_n = n)
    (max) 的方案数,放在平面上折线法计数就行了。

    时间复杂度 (O(n + T))

    (Source)

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    typedef long long ll;
    int in() {
        int x = 0; char c = getchar(); bool f = 0;
        while (c < '0' || c > '9')
            f |= c == '-', c = getchar();
        while (c >= '0' && c <= '9')
            x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
        return f ? -x : x;
    }
    ll lin() {
        ll x = 0; char c = getchar(); bool f = 0;
        while (c < '0' || c > '9')
            f |= c == '-', c = getchar();
        while (c >= '0' && c <= '9')
            x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
        return f ? -x : x;
    }
    template<typename T>void out(T _) {
        if (_ > 9)
            out(_ / 10);
        putchar(_ % 10 + 48);
    }
    template<typename T>inline void out_ln(T _) {
        out(_), putchar('
    ');
    }
    template<typename T>inline void chk_min(T &_, T __) { _ = _ < __ ? _ : __; }
    template<typename T>inline void chk_max(T &_, T __) { _ = _ > __ ? _ : __; }
    
    const int N = 2e7 + 5, mod = 1e9 + 7;
    int n, x, y;
    int fac[N], inv[N];
    
    int qpow(int base, int b, int ret = 1) {
        for (; b; b >>= 1, base = 1ll * base * base % mod)
            if (b & 1)
                ret = 1ll * ret * base % mod;
        return ret;
    }
    
    void prep() {
        fac[0] = inv[0] = 1;
        for (int i = 1; i < N; ++i)
            fac[i] = 1ll * fac[i - 1] * i % mod;
        inv[N - 1] = qpow(fac[N - 1], mod - 2);
        for (int i = N - 2; i; --i)
            inv[i] = 1ll * inv[i + 1] * (i + 1) % mod;
    }
    
    inline int C(const int n, const int m) {
        return n < m ? 0 : 1ll * fac[n] * inv[m] % mod * inv[n - m] % mod;
    }
    
    int main() {
        //freopen("in", "r", stdin);
        freopen("tg.in", "r", stdin);
        freopen("tg.out", "w", stdout);
        int T = in(), res = 0;
        prep();
        while (T--) {
            n = in(), x = in(), y = in();
            if (y < x)
                std::swap(x, y);
            res = 1ll * (C(x + y - 2, x - 1) - C(x + y - 2, x - 2)) * (C(n - x + n - y, n - x) - C(n - x + n - y, n - x + 1)) % mod;
            if (res < 0)
                res += mod;
            out_ln(res);
        }
        return 0;
    }
    
  • 相关阅读:
    [转载]解析用户生命周期价值:LTV
    [整理]VS2013常用插件
    Extjs4 tabpanel 中表单定宽居中
    idea创建ssm项目
    shiro框架的学习
    mysql
    springboot
    idea配置
    Spring MVC
    关于web乱码问题
  • 原文地址:https://www.cnblogs.com/15owzLy1-yiylcy/p/11361976.html
Copyright © 2011-2022 走看看