牛客小白月赛22

题目传送门

官方题解传送门

A .操作序列

sol:如果STL熟悉,那么就是一道模拟题。就是输入有点奇葩。但是看了官方题解中提到了平衡树。嗯,没错,map和set的底层都是平衡树。红黑树不会,平衡树觉得还是fhq-treap好敲。所以,提供一份map的解法和一份fhq-treap的解法。

  • map
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef pair<int, int> PII;
    map<int, int> mp;
    int main() {
        int t; scanf("%d", &t);
        while (t--) {
            int n, m, op; char c;
            scanf("%d%c", &n, &c);
            if (c == ' ') {
                scanf("%d", &m);
                bool ok = true;
                for (int i = n - 30; i <= n + 30; i++)
                    if (mp.count(i)) ok = false;
                if (ok) mp[n] = m;
                continue;
            }
            if (n == -1) {
                if (mp.empty()) puts("skipped");
                else {
                    printf("%d\n", mp.begin()->second);
                    mp.erase(mp.begin());
                }
            } else {
                if (mp.count(n)) printf("%d\n", mp[n]);
                else puts("0");
            }
        }
        return 0;
    }
    View Code

     

  • fhq-treap
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef pair<int, int> PII;
    const int MAXN = 1e6 + 10;
    struct Treap {
        int key, val;
        int rand;
        int lson, rson;
    } node[MAXN];
    int root, tot;
    int new_node(int n, int k) {
        int i = ++tot;
        node[i].key = n;
        node[i].val = k;
        node[i].rand = rand();
        node[i].lson = node[i].rson = 0;
        return i;
    }
    void split(int rt, int& a, int& b, int k) {
        if (rt == 0) {
            a = b = 0;
            return;
        }
        if (node[rt].key <= k) {
            a = rt;
            split(node[rt].rson, node[a].rson, b, k);
        } else {
            b = rt;
            split(node[rt].lson, a, node[b].lson, k);
        }
    }
    void merge(int& rt, int a, int b) {
        if (a == 0 || b == 0) {
            rt = a + b;
            return;
        }
        if (node[a].rand < node[b].rand) {
            rt = a;
            merge(node[rt].rson, node[a].rson, b);
        } else {
            rt = b;
            merge(node[rt].lson, a, node[b].lson);
        }
    }
    void insert(int n, int k) {
        int a, b, c;
        split(root, root, c, n + 30);
        split(root, a, b, n - 30 - 1);
        if (b == 0) b = new_node(n, k);
        merge(root, a, b);
        merge(root, root, c);
    }
    int pop(int& point) {
        if (node[point].lson == 0) {
            int tmp = node[point].val;
            point = node[point].rson;
            return tmp;
        }
        return pop(node[point].lson);
    }
    int get_val(int n) {
        int a, b, c;
        split(root, root, c, n);
        split(root, a, b, n - 1);
        int tmp;
        if (b == 0) tmp = 0;
        else tmp = node[b].val;
        merge(root, a, b);
        merge(root, root, c);
        return tmp;
    }
    int main() {
        int t; scanf("%d", &t);
        while (t--) {
            int n, k; char c;
            scanf("%d%c", &n, &c);
            if (c == ' ') {
                scanf("%d", &k);
                insert(n, k);
            } else if (n == -1) {
                if (root == 0) puts("skipped");
                else printf("%d\n", pop(root));
            } else {
                printf("%d\n", get_val(n));
            }
        }
        return 0;
    }
    View Code

    fhq-treap真的是平衡树里最简单的了,这么久没敲还能凭对代码的记忆而不是代码的理解一次敲对。

B .树上子链

sol:和求树的直径差不多,树的直径是求树上最长的链,这题转化成求权值最大的链。

  • 树的直径
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef pair<int, int> PII;
    const LL INF = 0x3f3f3f3f3f3f3f3f;
    const int MAXN = 1e5 + 10;
    vector<int> edge[MAXN];
    int val[MAXN];
    LL res = -INF;
    LL dfs(int u, int f) {
        LL max1 = 0, max2 = 0;
        for (int v : edge[u]) {
            if (v == f) continue;
            LL val = dfs(v, u);
            if (val > max2) max2 = val;
            if (max2 > max1) swap(max1, max2);
        }
        res = max(res, max1 + max2 + val[u]);
        return max1 + val[u] > 0 ? max1 + val[u] : 0;
    }
    int main() {
        int n; scanf("%d", &n);
        for (int i = 1; i <= n; i++)
            scanf("%d", &val[i]);
        for (int i = 2; i <= n; i++) {
            int u, v;
            scanf("%d%d", &u, &v);
            edge[u].push_back(v);
            edge[v].push_back(u);
        }
        dfs(1, -1);
        printf("%lld\n", res);
        return 0;
    }
    View Code

     

C .交换游戏

sol:看了题解后感觉是一个挺裸的记忆化搜索。补掉了。

  • 记忆化搜索
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef pair<int, int> PII;
    const int MAXN = 1 << 12 | 10;
    int dp[MAXN];
    int read() {
        int n = 0;
        char c = getchar();
        while (c != '-' && c != 'o') c = getchar();
        while (c == '-' || c == 'o') {
            n = n << 1 | (c == 'o');
            c = getchar();
        }
        return n;
    }
    int dfs(int n) {
        if (dp[n] != -1) return dp[n];
        int tmp = n, cnt = 0;
        while (tmp) {
            cnt ++;
            tmp -= tmp & -tmp;
        }
        dp[n] = cnt;
        int a = 1, b = 2, c = 4;
        for (int i = 1; i <= 10; i++) {
            if ((n & a) && (n & b) && !(n & c)) {
                dp[n] = min(dp[n], dfs(n ^ a ^ b ^ c));
            }
            if (!(n & a) && (n & b) && (n & c)) {
                dp[n] = min(dp[n], dfs(n ^ a ^ b ^ c));
            }
            a <<= 1, b <<= 1, c <<= 1;
        }
        return dp[n];
    }
    int main() {
        memset(dp, -1, sizeof(dp));
        int t; scanf("%d", &t);
        while (t--) {
            int n = read();
            printf("%d\n", dfs(n));
        }
        return 0;
    }
    View Code

     

D .收集纸片

sol:比赛的时候想着爆搜整张地图然后代码也没实现出来,题目中的纸片最多只有10张,所以突破口在纸片,可以枚举所以的收集顺序。那么枚举的方法就有很多了。官方题解中用了dfs的方法,那么我这里就用next_premutation这个函数来解决了。这个函数在蓝桥杯中也被多次考到。

  • 全排列
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef pair<int, int> PII;
    const int INF = 0x3f3f3f3f;
    PII p[20]; int order[20];
    int main() {
        int t; scanf("%d", &t);
        while (t--) {
            scanf("%*d%*d%d%d", &p[0].first, &p[0].second);
            int n; scanf("%d", &n);
            for (int i = 1; i <= n; i++) {
                scanf("%d%d", &p[i].first, &p[i].second);
                order[i] = i;
            }
            order[n + 1] = 0;
            int res = INF;
            do {
                int sum = 0;
                for (int i = 1; i <= n + 1; i++) {
                    sum += abs(p[order[i]].first - p[order[i - 1]].first);
                    sum += abs(p[order[i]].second - p[order[i - 1]].second);
                }
                if (sum < res) res = sum;
            } while (next_permutation(order + 1, order + 1 + n));
            printf("The shortest path has length %d\n", res);
        }
        return 0;
    }
    View Code

    原来给出的地图大小是没用的

E .方块涂色

sol:总共$n$行,被涂了$r$行,还剩$(n - r)$行。总共$m$列,被涂了$c$列,还剩$(m - c)$列,所以答案就是$(n - r) * (m - c)$。注意到结果可能爆int就不会错了。

  • 数学
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef pair<int, int> PII;
    int main() {
        int n, m, r, c;
        while (~scanf("%d%d%d%d", &n, &m, &r, &c)) 
            printf("%lld\n", 1LL * (n - r) * (m - c));
        return 0;
    }
    View Code

     

F .累乘数字

sol:听说有人拿到题就直接用python高精度上了。也是一种不错的选择,容易抢一血。不过更高效的方法就是输出$n$之后再输出$d$次$00$。

  • python大数
    while True :
        try :
            n, m = map(int, input().split())
            print(n * 100 ** m)
        except : 
            break
    View Code

     

  • 数学
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef pair<int, int> PII;
    int main() {
        int n, m;
        while (~scanf("%d%d", &n, &m)) {
            printf("%d", n);
            for (int i = 1; i <= m; i++)
                printf("00");
            puts("");
        }
        return 0;
    }
    View Code

     

G .仓库选址

sol:一种比较直白的方式是枚举每个位置做仓库,然后算出这个位置的花费,然后找出最小值。复杂度是$O((n * m) * (n * m))$。官方题解就是这种方法,参照官方题解。但是其实可以把行和列分开考虑使复杂度变成$O((n * m) + (n * m))$。

  • 思维
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef pair<LL, LL> PII;
    const LL INF = 0x3f3f3f3f;
    const LL MAXN = 110;
    LL row[MAXN], col[MAXN];
    LL cal(LL* arr, LL len) {
        LL res = INF;
        for (LL i = 1; i <= len; i++) {
            LL sum = 0;
            for (LL j = 1; j <= len; j++)
                sum += abs(i - j) * arr[j];
            res = min(res, sum);
        }
        return res;
    }
    int main() {
        LL t; scanf("%lld", &t);
        while (t--) {
            LL n, m, k;
            scanf("%lld%lld", &m, &n);
            memset(row, 0, sizeof(row));
            memset(col, 0, sizeof(col));
            for (LL i = 1; i <= n; i++) {
                for (LL j = 1; j <= m; j++) {
                    scanf("%lld", &k);
                    row[i] += k;
                    col[j] += k;
                }
            }
            printf("%lld\n", cal(row, n) + cal(col, m));
        }
        return 0;
    }
    View Code

     

H .货物种类

sol:关键是差分,然后我是用map来维护的

  • 差分
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef pair<int, int> PII;
    const int MAXN = 1e5 + 10;
    map<int, int> pre[MAXN], now;
    int main() {
        int n, m;
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= m; i++) {
            int l, r, d;
            scanf("%d%d%d", &l, &r, &d);
            pre[l][d] ++;
            pre[r + 1][d] --;
        }
        int res, cnt = 0;
        for (int i = 1; i <= n; i++) {
            for (auto it : pre[i]) {
                now[it.first] += it.second;
                if (now[it.first] == 0)
                    now.erase(it.first);
            }
            if (now.size() > cnt) {
                cnt = now.size();
                res = i;
            }
        }
        printf("%d\n", res);
        return 0;
    }
    View Code

     

I .工具人

 

J .计算A + B

sol:skipped的情况有'+'在最前面,'+'在最后面,'+'的次数不为1。剩下的就是高精度了。反正我是用python水过去的,python大数和split真香。至于C++,之后再补上吧。

  • python大数
    t = int(input())
    for i in range(t) :
        n = input().split('+')
        if len(n) != 2 or n[0] == '' or n[1] == '':
            print('skipped');
        else :
            print(int(n[0]) + int(n[1]))
    View Code

     

ps:一血的代码太精华了,佩服佩服,贴个传送门

--------------------------------------------------前来填坑--------------------------------------------------

  • 高精度模拟
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef pair<int, int> PII;
    const int MAXN = 10010;
    struct BigInt {
        const static int mod = 10000;
        const static int dlen = 4;
        int a[600], len;
        BigInt() {
            memset(a, 0, sizeof(a));
            len = 1;
        }
        BigInt(const char* s) {
            memset(a, 0, sizeof(a));
            int l = strlen(s); len = 0;
            for (int i = l - 1; i >= 0; i -=  dlen) {
                int tmp = 0, start = max(0, i - 4 + 1);
                for (int j = start; j <= i; j++)
                    tmp = tmp * 10 + (s[j] ^ '0');
                a[len ++] = tmp;
            }
        }
        friend BigInt operator + (BigInt a, BigInt b) {
            BigInt res;
            res.len = max(a.len, b.len);
            int tmp = 0;
            for (int i = 0; i < res.len; i++) {
                tmp += a.a[i] + b.a[i];
                res.a[i] = tmp % mod;
                tmp /= mod;
            }
            if (tmp) res.a[res.len ++] = tmp;
            return res;
        }
        void output() {
            printf("%d", a[len - 1]);
            for (int i = len - 2; i >= 0; i--)
                printf("%04d", a[i]);
            puts("");
        }
    };
    char s[MAXN];
    int find_plus(const char* s) {
        int res = -1;
        for (int i = 0; s[i]; i++) {
            if (s[i] == '+') {
                if (res != -1) return -1;
                else res = i;
            }
        }
        return res;
    }
    int main() {
        int t; scanf("%d", &t);
        while (t--) {
            scanf("%s", s);
            int index = find_plus(s);
            if (index == -1 || index == 0 || s[index + 1] == '\0') {
                puts("skipped");
            } else {
                s[index] = '\0';
                BigInt a(s), b(s + index + 1);
                BigInt res = a + b;
                res.output();
            }
        }
        return 0;
    }
    View Code

    仿照kuangbin大佬的模板代码自己写了一个比较骚的高精度。中间写错了两次。看样子还是很有必要写这份C++版的。

posted @ 2020-02-23 22:22  Angel_Demon  阅读(...)  评论(...编辑  收藏