• 小Z 系列 解题报告


    在你谷刷题时偶然发现有这么一个系列,大概(15)道题目左右。

    而且蒟蒻发现,这个系列的出题人基本上全是LittleZ大佬OrzOrz

    于是心血来潮,想把这个系列全部写完,然后便有了本文。


    (P.s.:)按蒟蒻自己的做题顺序排列,不一定代表难易。

    (1.)小Z的矩阵

    因为题目中给出的特征函数(G(A))是对(2)取模后的结果,所以很容易就可以发现除对角线以外所有元素对答案的贡献均为(0),所以第一步单累计对角线贡献即可。

    然后我们发现每翻转一行一列对答案无影响,即刚好翻转了所求的和(()(mod 2)的条件下()),所以每次翻转直接取异或即可。

    code:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    
    int n, q, g, x;
    
    int main() {
        scanf("%d%d", &n, &q);
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                scanf("%d", &x);
                if (i == j) g = (g + x) % 2; 
            }
        }
        for (int i = 1; i <= q; i++) {
            scanf("%d", &x);
            if (x == 3) printf("%d", g);
            else {
                scanf("%d", &x);
                g ^= 1;
            }
        }
        return 0;
    }
    
    

    (2.)小Z的k紧凑数

    最近刚学数位(DP)就看到了这道题(……)而且简直和windy数一模一样

    具体处理方法和(windy)数基本一样,不了解数位(DP)的可以去看蒟蒻整理的学习笔记,内含(windy)数的详细题解。

    code:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #define int long long
    #define abs(a) ((a) < 0 ? -(a) : (a))
    
    int f[25][15], num[25], a, b, opt;
    
    void init() {
        for (int i = 0; i <= 9; i++) f[1][i] = 1;
        for (int i = 2; i <= 20; i++) {
            for (int j = 0; j <= 9; j++) {
                for (int k = 0; k <= 9; k++)
                    if (abs(j - k) <= opt) f[i][j] += f[i - 1][k];
            }
        }
    }
    
    int calc(int x) {
        int len = 0, ans = 0;
        memset(num, 0, sizeof(num));
        while (x) {
            num[++len] = x % 10;
            x /= 10;
        }
        for (int i = 1; i <= len - 1; i++) {
            for (int j = 1; j <= 9; j++)
                ans += f[i][j];
        }
        for (int i = len; i >= 1; i--) {
            for (int j = 0; j < num[i]; j++) {
                if (i == len && !j) continue;
                if (abs(num[i + 1] - j) <= opt || i == len) ans += f[i][j];
            }
            if (abs(num[i + 1] - num[i]) > opt && i != len) break;
        }
        return ans;
    }
    
    signed main() {
        scanf("%lld%lld%lld", &a, &b, &opt);
        init();
        printf("%lld
    ", calc(b + 1) - calc(a));
        return 0;
    }
    
    
    

    (3.)小Z的车厢

    根据题意,我们只需要统计一下同一时刻在列车上最多有多少人即可,然后若人数(mod 36)为零,直接输出人数(/36),否则再(+1)

    但是我们要注意,这题的轨道是环形的,所以就会出现这种情况:

    [4->3 ]

    路线应是:

    [4->1->2->3 ]

    然后我们考虑一下,这种情况应该怎么处理。

    仔细想一下我们发现:这种情况似乎等价于在(4)号下车,再在(1)号上车,所以我们每次遇到(x>y)的情况时,直接在一号点多累加一次即可。

    code:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #define max(a, b) ((a) > (b) ? (a) : (b))
    
    const int maxn = 1e6 + 6;
    int on[maxn], off[maxn], n, m, x, y, z;
    
    int main() {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= m; i++) {
            scanf("%d%d%d", &x, &y, &z);
            on[x] += z;
            off[y] += z;
            if (x > y)
                on[1] += z;
        }
        int ans = 0, sum = 0;
        for (int i = 1; i <= n; i++) {
            sum += on[i];
            sum -= off[i];
            ans = max(ans, sum);
        }
        if (ans % 36 == 0)
            std::cout << ans / 36 << '
    ';
        else std::cout << ans / 36 + 1 << '
    ';
        return 0;
    }
    
    

    (4.)小Z的情书

    比较好的一道模拟题目(……)

    由样例我们可以看出来上面的那张透明纸片是顺时针旋转的,然后把每次翻转后点写下来会发现它是有规律的:

    [(i,j)Rightarrow (j,n-i+1) ]

    所以我们每次将当前可看到的累加到答案中,然后按规律将坐标进行变换,进行三次就好惹。

    突然想到,这个规律貌似和数学上笛卡尔坐标系中的点顺时针翻转(90)度的规律类似(……)

    code:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    
    using std::string;
    int n;
    string ans;
    char s[1007][1007], ch;
    bool p[1007][1007], q[1007][1007];
    
    void reverse() {
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                q[i][j] = 0;
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                if (p[i][j] == 1) q[j][n - i + 1] = 1;
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                p[i][j] = q[i][j];
    }
    
    int main() {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++) {
                std::cin >> ch;
                if (ch == 'O') p[i][j] = 1;
        }
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                std::cin >> s[i][j];
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                if (p[i][j]) ans += s[i][j];
        reverse();
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                if (p[i][j]) ans += s[i][j];
        reverse();
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                if (p[i][j]) ans += s[i][j];
        reverse();
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                if (p[i][j]) ans += s[i][j];
        std::cout << ans << '
    ';
        return 0;
    }
    
    

    (5.)小Z的笔记

    设计状态:令(f[i])表示前(i)个位置变为合法状态最少要删的个数。

    朴素的(DP)转移方程:

    [f[i]=min{f[j]-j+i-1},jepsilon[a,z] ]

    表示当(i)(j)可以相邻时,将(isim j)的所有字母全部删除。

    然后很显然这个转移是(O(n^2))的,所以我们考虑如何优化。

    我们先将有关(i)的部分全部提出来,方程变为:

    [f[i]=min{f[j]-j}+i-1 ]

    同时,我们发现只有(26)个字母,所以我们对每个字母维护一个数组(g[i]),表示:

    [g[j] = min{f[j]-j} ]

    虽然有两层循环,但第二层只有(26),因此总时间复杂度为(O(26n))

    code:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <map>
    #include <algorithm>
    
    using std::map;
    using std::string;
    const int maxn = 1e5 + 5;
    bool vis[55][55];
    int f[maxn], n, m, g[maxn];
    string x;
    char s[100005];
    
    int main() {
        scanf("%d", &n);
        scanf("%s", s + 1);
        scanf("%d", &m);
        for (int i = 1; i <= m; i++) {
            std::cin >> x;
            vis[x[0] - 'a'][x[1] - 'a'] = vis[x[1] - 'a'][x[0] - 'a'] = true;
        }
        memset(f, 0x3f, sizeof(f));
        f[1] = 0;
        g[s[1] - 'a'] = 0 - 1;
        for (int i = 2; i <= n; i++) {
            for (int j = 0; j < 26; j++) {
                if (vis[s[i] - 'a'][j]) continue;
                f[i] = std::min(f[i], g[j] + i - 1);
            }
            g[s[i] - 'a'] = std::min(g[s[i] - 'a'], f[i] - i);
        }
        printf("%d
    ", f[n]);
        return 0;
    }
    
    //f[i] = min{f[j] - j} + i - 1;
    
    

    (6.)小Z的栈函数

    一道(……)恶心的模拟工业(……)

    没什么思路可说,按照题目所说的模拟即可,可以选择(STL)(stack),也可以选择像蒟蒻一样手写一个栈。

    但是(……)一定要注意细节!!!一定要注意细节!!!一定要注意细节!!!

    简单列举下需要判断的:

    • 栈内元素个数不够这次操作所需

    • 中途出现绝对值大于(1e9)的数

    • 除或模时要判断除数是否为(0)

    然后基本上就没什么了,但它很恶心(……)

    没有封装又丑陋无比的代码……

    code:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #define int long long
    #define abs(a) ((a) < 0 ? -(a) : (a))
    
    using std::string;
    string opt;
    int st[100050], top, n, m, fr;
    
    struct Node {
        string x;
        int num;
    }a[2005];
    
    signed main() {
        while (1) {
            ++n;
            std::cin >> a[n].x;
            if (a[n].x == "END") break;
            if (a[n].x == "NUM") scanf("%d", &a[n].num);
        }
        scanf("%d", &m);
        bool flag = false;
        for (int j = 1; j <= m; j++) {
            top = 0; flag = false;
            scanf("%d", &fr);
            if (fr > 1000000000) {puts("ERROR"); continue;}
            st[++top] = fr;
            for (int i = 1; i <= n; i++) {
                if (a[i].x == "NUM") {
                    if (a[i].num > 1000000000) {flag = true; break;}
                    st[++top] = a[i].num;
                }
                else if (a[i].x == "POP") {
                    if (top == 0) {flag = true; break;}
                    else top--;
                }
                else if (a[i].x == "INV") 
                    if (top == 0) {flag = true; break;}
                    else {int temp = st[top--]; st[++top] = -temp;}
                else if (a[i].x == "DUP") 
                    if (top == 0) {flag = true; break;}
                    else {int temp = st[top]; st[++top] = temp;}
                else if (a[i].x == "SWP") 
                    if (top < 2) {flag = true; break;}
                    else std::swap(st[top], st[top - 1]);
                else if (a[i].x == "ADD") 
                    if (top < 2) {flag = true; break;}
                    else {
                        int temp1 = st[top--], temp2 = st[top--];
                        if (abs(temp1 + temp2) > 1000000000) {flag = true; break;}
                        st[++top] = temp1 + temp2;
                    }
                else if (a[i].x == "SUB") 
                    if (top < 2) {flag = true; break;}
                    else {
                        int temp1 = st[top--], temp2 = st[top--];
                        if (abs(temp2 - temp1) > 1000000000) {flag = true; break;}
                        st[++top] = temp2 - temp1;
                    }
                else if (a[i].x == "MUL") 
                    if (top < 2) {flag = true; break;}
                    else {
                        int temp1 = st[top--], temp2 = st[top--];
                        if (abs(temp1 * temp2) > 1000000000) {flag = true; break;}
                        st[++top] = temp1 * temp2;
                    }
                else if (a[i].x == "DIV") 
                    if (top < 2) {flag = true; break;}
                    else {
                        int temp1 = st[top--], temp2 = st[top--];
                        if (temp1 == 0) {flag = true; break;}
                        if (abs(temp2 / temp1) > 1000000000) {flag = true; break;}
                        st[++top] = temp2 / temp1;
                    }
                else if (a[i].x == "MOD") 
                    if (top < 2) {flag = true; break;}
                    else {
                        int temp1 = st[top--], temp2 = st[top--];
                        if (temp1 == 0) {flag = true; break;}
                        if (abs(temp2 % temp1) > 1000000000) {flag = true; break;}
                        st[++top] = temp2 % temp1;
                    }
            }
            if (flag || top != 1) puts("ERROR");
            else std::cout << st[top] << '
    ';
        }
        return 0;
    }
    

    (7.)小Z的AK计划

    算法分析标签(:)二叉堆优先队列

    这道题本蒟蒻的第一想法是贪心,以一个点坐标与题目数量的乘积为关键字,但这个想法是很容易被(hack)的,而且这也没有办法考虑走回去的情况。

    那么来找一下本题的突破口,题面中有这样一句话(:)当然,也可以过机房而不入,那么顺着这句话,我们考虑,能否一路走下去,不考虑回头,只考虑进不进入某一机房。

    那么我们用一个大根堆优先队列来记录进入过哪些机房,每进入一个新机房,我们统计至今为止所有到过机房所耗的总时间,然后加上当前点坐标,若(>m)不断减去队首同时机房数量(--),当这一操作进行完后更新答案即可。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <queue>
    #include <algorithm>
    #define int long long
    #define max(a, b) ((a) > (b) ? (a) : (b))
    
    std::priority_queue<int>q;
    const int maxn = 1e5 + 5;
    int n, m, tot, sum, ans;
    
    struct Node {
        int x, t;
        bool operator < (const Node &rhs) const {
            return x < rhs.x;
        }
    }a[maxn];
    
    template<class T>
    inline T read(T &x) {
        x = 0; int w = 1, ch = getchar();
        while (ch < '0' || ch > '9') {if (ch == '-') w = -1; ch = getchar();}
        while (ch >= '0' && ch <= '9') {x = x * 10 + ch - 48; ch = getchar();}
        return x *= w;
    }
    
    signed main() {
        read(n), read(m);
        for (int i = 1; i <= n; i++)
            read(a[i].x), read(a[i].t);
        std::sort(a + 1, a + n + 1);
        for (int i = 1; i <= n; i++) {
            if (a[i].x > m) break;
            q.push(a[i].t);
            ++tot;
            sum += a[i].t;
            while (!q.empty() && sum + a[i].x > m) {
                --tot;
                sum -= q.top();
                q.pop();
            }
            ans = max(ans, tot);
        }
        printf("%lld
    ", ans);
        return 0;
    }
    
    

  • 相关阅读:
    蓝牙低功耗(Bluetooth Low Energy)
    Android 蓝牙(概述)
    Android 学习笔记之 Activity 简介
    Android 学习笔记之常用控件
    Android 学习笔记之界面布局
    委托和事件(C#)
    Java 资源汇总
    如何阅读英文原版教材
    Combobox 控件绑定数据
    《将博客搬至CSDN》
  • 原文地址:https://www.cnblogs.com/Hydrogen-Helium/p/11737950.html
Copyright © 2020-2023  润新知