• 2022.1.18 模拟赛


    哈哈输出大样例能得的分都比打了 3.5h 得分高。

    A. 「清华集训 2017」小 Y 和恐怖的奴隶主

    期望dp + 矩阵乘法

    看完题之后比较晕,没有想到把 1,2,3 血的怪的数量都记录下来,整个 4 维期望 dp,直接跳过了,有点亏。

    设状态 \(dp[i][a][b][c]\),表示打了 \(i\) 次之后场上 1,2,3 血的怪物分别有 \(a, b, c\) 只。

    暴力转移显然会 TLE。

    不难发现,总共只有165 个状态,加上答案状态就是 166 个状态,所以可以编个号,然后压到一维里,使用矩乘加速。

    code
    #include <bits/stdc++.h>
    #define ll long long
    
    using namespace std;
    
    namespace IO{
        inline ll read(){
            ll x = 0;
            char ch = getchar();
            while(!isdigit(ch)) ch = getchar();
            while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
            return x;
        }
    
        template <typename T> inline void write(T x){
            if(x < 0) putchar('-'), x = -x;
            if(x > 9) write(x / 10);
            putchar(x % 10 + '0');
        }
    }
    using namespace IO;
    
    const int N = 210;
    const int mod = 998244353;
    int T, m, s, n;
    int id[13][13][13], inv[N];
    
    inline int add(int x) {return x >= mod ? x - mod : x;}
    
    inline int qpow(int a, int b){
        int res = 1;
        while(b){
            if(b & 1) res = 1ll * res * a % mod;
            a = 1ll * a * a % mod, b >>= 1;
        }
        return res;
    }
    
    struct matrix{
        int num[N][N];
    
        matrix() {memset(num, 0, sizeof(num));}
    
        void init() {for(int i = 1; i <= n; ++i) num[i][i] = 1;}
    
        friend matrix operator * (matrix a, matrix b){
            matrix c;
            for(int i = 1; i <= n; ++i)
                for(int j = 1; j <= n; ++j)
                    for(int k = 1; k <= n; ++k)
                        c.num[i][j] = add(c.num[i][j] + 1ll * a.num[i][k] * b.num[k][j] % mod);
            return c;
        }
    }f[64];
    int ans[N], t[N];
    
    inline void Mul(int u){
        for(int i = 1; i <= n; ++i) t[i] = 0;
        for(int i = 1; i <= n; ++i)
            for(int j = 1; j <= n; ++j)
                t[i] = add(t[i] + 1ll * ans[j] * f[u].num[j][i] % mod);
        for(int i = 1; i <= n; ++i) ans[i] = t[i];
    }
    
    int main(){
        T = read(), m = read(), s = read();
        for(int i = 1; i <= 9; ++i) inv[i] = qpow(i, mod - 2);
        for(int i = 0; i <= s; ++i)
            for(int j = 0; j <= (m >= 2 ? s - i : 0); ++j)
                for(int k = 0; k <= (m == 3 ? s - i - j : 0); ++k)
                    id[i][j][k] = ++n;
        n++;
        f[0].num[n][n] = 1;
        for(int a = 0; a <= s; ++a)
            for(int b = 0; b <= (m >= 2 ? s - a : 0); ++b)
                for(int c = 0; c <= (m == 3 ? s - a - b : 0); ++c){
                    int i = id[a][b][c], inv_i = inv[a + b + c + 1], t = (a + b + c < s);
                    if(a) f[0].num[i][id[a - 1][b][c]] = 1ll * a * inv_i % mod;
                    if(m == 2 && b) f[0].num[i][id[a + 1][b - 1 + t][c]] = 1ll * b * inv_i % mod;
                    if(m == 3 && b) f[0].num[i][id[a + 1][b - 1][c + t]] = 1ll * b * inv_i % mod;
                    if(m == 3 && c) f[0].num[i][id[a][b + 1][c - 1 + t]] = 1ll * c * inv_i % mod;
                    f[0].num[i][i] = f[0].num[i][n] = inv_i;
                }
        for(int i = 1; i <= 60; ++i) f[i] = f[i - 1] * f[i - 1];
        while(T--){
            ll x = read();
            for(int i = 1; i <= n; ++i) ans[i] = 0;
            ans[id[m == 1][m == 2][m == 3]] = 1;
            for(int i = 0; i <= 60; ++i)
                if((x >> i) & 1) Mul(i);
            write(ans[n]), puts("");
        }
        return 0;
    }
    

    B. 「一本通 4.4 练习 4」跳跳棋

    巨大难思维 + LCA

    谁能想到这题跳棋的情况可以建成一棵树???

    20pts bfs 暴力滚粗。

    注意题目里:一次只允许跳过一个棋子。

    (考场上虽然看到了,但是没反应过来,还以为是只能跳一次的意思……)

    所以中间的点可以向两边跳,两边的点只有一个能往中间跳(只有距离中间那个点近的点可以向中间跳)。

    把三个点的位置三元组 \((x, y, z)\) 存到一个节点里,那么是如何建树的呢?

    把向中间跳的那个三元组当父亲节点,向外跳的两个当儿子节点,然后两点之间的距离就是跳的次数。

    但是怎么快速求 LCA 呢?三元组普通的倍增什么的是不行的,所以先把深度大的点跳到跟另一个点深度相同的高度,然后二分向上跳的步数,再判断。

    code
    #include <bits/stdc++.h>
    
    using namespace std;
    
    namespace IO{
        inline int read(){
            int x = 0, f = 1;
            char ch = getchar();
            while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
            while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
            return x * f;
        }
    
        template <typename T> inline void write(T x){
            if(x > 9) write(x / 10);
            putchar(x % 10 + '0');
        }
    }
    using namespace IO;
    
    struct node{
        int x, y, z;
    
        inline void init(){
            if(x > y) swap(x, y);
            if(x > z) swap(x, z);
            if(y > z) swap(y, z);
        }
    
        friend bool operator != (node a, node b){
            return (a.x ^ b.x) | (a.y ^ b.y) | (a.z ^ b.z);
        }
    
        friend bool operator == (node a, node b){
            return (a.x == b.x) & (a.y == b.y) & (a.z == b.z);
        }
    }a, b, A, B, C;
    int tot1, tot2, ans;
    
    inline int dfs(int x, int y, int z){
        int d1 = y - x, d2 = z - y, cnt = 0, d;
        if(d1 > d2){
            cnt = d1 / d2, d = d1 % d2;
            if(!d) d += d2, cnt--;
            cnt += dfs(x, x + d, x + d + d2);
        }else if(d1 < d2){
            cnt = d2 / d1, d = d2 % d1;
            if(!d) d += d1, cnt--;
            cnt += dfs(z - d - d1, z - d, z);
        }else C = (node){x, y, z};
        return cnt;
    }
    
    inline void update(int x, int y, int z, int step){
        if(!step) return C = (node){x, y, z}, void();
        int d1 = y - x, d2 = z - y, cnt = 0, d;
        if(d1 > d2){
            cnt = d1 / d2, d = d1 % d2;
            if(!d) d += d2, cnt--;
            if(step >= cnt) update(x, x + d, x + d + d2, step - cnt);
            else update(x, x + d + d2 * (cnt - step), x + d + d2 * (cnt - step + 1), 0);
        }else if(d1 < d2){
            cnt = d2 / d1, d = d2 % d1;
            if(!d) d += d1, cnt--;
            if(step >= cnt) update(z - d - d1, z - d, z, step - cnt);
            else update(z - d - d1 * (cnt - step + 1), z - d - d1 * (cnt - step), z, 0);
        }else C = (node){x, y, z};
    }
    
    inline bool check(int step){
        update(a.x, a.y, a.z, step), A = C;
        update(b.x, b.y, b.z, step), B = C;
        return A == B;
    }
    
    inline int solve(){
        int l = 0, r = min(tot1, tot2), res;
        while(l <= r){
            int mid = (l + r) >> 1;
            if(check(mid)) r = mid - 1, res = mid;
            else l = mid + 1;
        }
        return (res << 1) + ans;
    }
    
    int main(){
        a = (node){read(), read(), read()}, b = (node){read(), read(), read()};
        a.init(), b.init();
        tot1 = dfs(a.x, a.y, a.z), A = C;
        tot2 = dfs(b.x, b.y, b.z), B = C;
        // cout << tot1 << " " << tot2 << endl;
        if(A != B) return puts("NO"), 0;
        if(tot1 < tot2){
            ans += tot2 - tot1;
            update(b.x, b.y, b.z, tot2 - tot1), b = C;
        }else if(tot1 > tot2){
            ans += tot1 - tot2;
            update(a.x, a.y, a.z, tot1 - tot2), a = C;
        }
        // cout << a.x << " " << a.y << " " << a.z << endl;
        printf("YES\n%d\n", solve());
        return 0;
    }
    

    C. Calc

    dp + 拉格朗日插值

    先考虑暴力 dp

    \(dp_{i, j}\) 表示前 \(i\) 个数用了 \(1 \sim j\) 的和,转移方程比较显然:

    我们只处理递增的序列,分类讨论第 \(i\) 个数取不取 \(j\)

    \[dp_{i, j} = dp_{i - 1, j - 1} \times j + dp_{i, j - 1} \]

    经过一系列推导,我们发现 \(dp_{n, i}\) 是关于 \(i\) 的一个 \(2n\) 次多项式,所以直接拉格朗日插值求一下 \(dp_{n, A}\) 即可。

    code
    #include <bits/stdc++.h>
    #define ll long long
    
    using namespace std;
    
    namespace IO{
        inline int read(){
            int x = 0;
            char ch = getchar();
            while(!isdigit(ch)) ch = getchar();
            while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
            return x;
        }
    
        template <typename T> inline void write(T x){
            if(x > 9) write(x / 10);
            putchar(x % 10 + '0');
        }
    }
    using namespace IO;
    
    const int N = 510;
    ll A, n, mod, ans;
    ll fac[N], f[N][N << 1];
    
    inline ll qpow(ll a, ll b){
        ll res = 1;
        while(b){
            if(b & 1) res = res * a % mod;
            a = a * a % mod, b >>= 1;
        }
        return res;
    }
    
    inline void lagrange(){
        for(int i = 0; i <= (n << 1); ++i){
            ll t1 = 1, t2 = 1;
            for(int j = 0; j <= (n << 1); ++j)
                if(i != j) t1 = t1 * (A - j + mod) % mod, t2 = t2 * (i - j + mod) % mod;
            ans = (ans + f[n][i] * t1 % mod * qpow(t2, mod - 2) % mod) % mod;
        }
    }
    
    int main(){
        A = read(), n = read(), mod = read();
        fac[0] = 1;
        for(int i = 1; i <= n; ++i) fac[i] = fac[i - 1] * i % mod;
        for(int i = 0; i <= (n << 1); ++i) f[0][i] = 1;
        for(int i = 1; i <= n; ++i)
            for(int j = 1; j <= (n << 1); ++j)
                f[i][j] = (f[i - 1][j - 1] * j % mod + f[i][j - 1]) % mod;
        lagrange();
        write(ans * fac[n] % mod), puts("");
        return 0;
    }
    
  • 相关阅读:
    sqlserver求标准偏差,oracle求标准偏差
    遇到 oracle 错误904 "maxsize" 标识符无效
    oracle添加自增字段
    oracle误删数据之表闪回
    浅淡ToString与Covert.ToString方法
    一段能用来统计ip访问的代码(自用)包括所在地
    Asp.Net 学习资源列表
    【META httpequiv="ContentType" Content="text/html; Charset=*】意义详解
    FreeTextBox treeview menu显示问题解决方法
    双边可关闭对联广告!
  • 原文地址:https://www.cnblogs.com/xixike/p/15827822.html
Copyright © 2020-2023  润新知