• Codeforces Global Round 5


    https://codeforces.com/contest/1237

    A - Balanced Rating Changes

    题意:给一堆和为0的数字,然后偶数直接除以2输出,奇数就除以2之后均匀分配上下整使得和依然是0。

    题解:奇数全部取下整,然后间隔着加。注意C++里面的整数除法是向0取整,假如全部除以2的话应该是错的,可以构造这样的:

    4
    -3
    -5
    4
    4
    

    这样两个奇数都是负的,然后就多了。

    void test_case() {
        int n;
        scanf("%d", &n);
        int a, b = 0;
        while(n--) {
            scanf("%d", &a);
            if(a & 1) {
                ++b;
                int t = floor(a / 2.0);
                t += (b & 1);
                printf("%d
    ", t);
            } else
                printf("%d
    ", a / 2);
        }
    }
    

    不想用浮点数的话就分正负判断吧,但是没必要,在这个4位整数的位数上面浮点数几乎没有误差。

    B - Balanced Tunnel

    题意:给一个入隧道的序列,一个出隧道的序列,判断谁在隧道里面超车。

    题解:先把题目给的反序变成从左到右看的顺序。首先最右边的车是不可能超车的。然后其他车假如他右侧的车的出隧道后的最左值在他左边,那么他一定进行了超车。

    int a[100005];
    int aa[100005];
    int b[100005];
    int bb[100005];
    int leftmost[100005];
    void test_case() {
        int n;
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i)
            scanf("%d", &a[i]);
        for(int i = 1; i <= n; ++i)
            scanf("%d", &b[i]);
        reverse(a + 1, a + 1 + n);
        reverse(b + 1, b + 1 + n);
        for(int i = 1; i <= n; ++i) {
            aa[a[i]] = i;
            bb[b[i]] = i;
        }
        leftmost[n] = bb[a[n]];
        for(int i = n - 1; i >= 1; --i)
            leftmost[i] = min(leftmost[i + 1], bb[a[i]]);
        int cnt = 0;
        for(int i = 1; i <= n - 1; ++i) {
            if(leftmost[i + 1] < bb[a[i]])
                ++cnt;
        }
        printf("%d
    ", cnt);
    }
    

    C2 - Balanced Removals (Harder)

    题意:给n个三维点,n是偶数。每次消去一对,要求每次消除的时候这两个点包围的矩形盒内没有其他点。

    题解:先按z分层,然后同一层按y分条,同一条按x排序,x相邻的两两之间没有其他点全部消去,多余的至多一个点传给上层。然后接下来的y相邻的之间的矩形也不会有其他点,两两消去。

    struct Point1 {
        int x, id;
        bool operator<(const Point1& p)const {
            return x < p.x;
        }
    } p1[50005];
    
    struct Point2 {
        int x, y, id;
        bool operator<(const Point2& p)const {
            return y < p.y;
        }
    } p2[50005];
    
    struct Point3 {
        int x, y, z, id;
        bool operator<(const Point3& p)const {
            return z < p.z;
        }
    } p3[50005];
    
    int solve_y(int L, int R) {
        int n = R - L + 1;
        for(int i = 1; i <= n; ++i) {
            p1[i].x = p2[L + i - 1].x;
            p1[i].id = p2[L + i - 1].id;
        }
        sort(p1 + 1, p1 + 1 + n);
        for(int i = 1; i + 1 <= n; i += 2)
            printf("%d %d
    ", p1[i].id, p1[i + 1].id);
        if(n & 1)
            return p1[n].id;
        return 0;
    }
    
    int solve_z(int L, int R) {
        int n = R - L + 1;
        for(int i = 1; i <= n; ++i) {
            p2[i].x = p3[L + i - 1].x;
            p2[i].y = p3[L + i - 1].y;
            p2[i].id = p3[L + i - 1].id;
        }
        sort(p2 + 1, p2 + 1 + n);
        queue<int> Q;
        int l = 1, r = 1;
        while(l <= n) {
            while(r + 1 <= n && p2[r + 1].y == p2[l].y)
                ++r;
            int p = solve_y(l, r);
            if(p) {
                Q.push(p);
                if(Q.size() == 2) {
                    printf("%d", Q.front());
                    Q.pop();
                    printf(" %d
    ", Q.front());
                    Q.pop();
                }
            }
            l = r + 1;
            r = l;
        }
        if(Q.size())
            return Q.front();
        return 0;
    }
    
    int n;
    void solve() {
        sort(p3 + 1, p3 + 1 + n);
        int l = 1, r = 1;
        queue<int> Q;
        while(l <= n) {
            while(r + 1 <= n && p3[r + 1].z == p3[l].z)
                ++r;
            int p = solve_z(l, r);
            if(p) {
                Q.push(p);
                if(Q.size() == 2) {
                    printf("%d", Q.front());
                    Q.pop();
                    printf(" %d
    ", Q.front());
                    Q.pop();
                }
            }
            l = r + 1;
            r = l;
        }
    }
    
    void test_case() {
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i) {
            scanf("%d%d%d", &p3[i].x, &p3[i].y, &p3[i].z);
            p3[i].id = i;
        }
        solve();
    }
    

    D - Balanced Playlist

    题意:给一个音乐序列,循环播放,对于每个位置i,计算以i为起始时会听多少音乐。当你听到一首严格小于你听过的歌的cool度的最大值的一半的时候就会停止,或者报告不会停止。

    题解:先观察,发现从全局最小开始听可以一直模拟出能听的最大值,一直搜索过去直到碰到底再一个一个回溯上来,或者报告听了一圈,假如听了一圈“成功”回到全局最小就说明可以一直听下去。否则回溯的时候就会把中间的所有的都更新?这样是错的,考虑下面的:

    5 3 11 6
    

    从5开始听到6就结束了,但是从6开始听到5才结束。注意到好像可以按非严格上升的序列来分组,同一组的肯定是贡献同一个结束位置的。而组与组之间有可能连上,一旦连上就会整组连上。和之前做的一场很像。但是怎么转移呢?

    int n;
    int a[200005], b[200005];
    int l[200005], r[200005], cnt;
    
    void test_case() {
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i) {
            scanf("%d", &a[i]);
            a[i + n] = a[i];
        }
        a[2 * n + 1] = a[1];
        int maxai = 0, minai = INF;
        for(int i = 1; i <= n; ++i) {
            if(a[i] < minai)
                minai = a[i];
            if(a[i] > maxai)
                maxai = a[i];
        }
        if(maxai <= 2 * minai) {
            for(int i = 1; i <= n; ++i)
                printf("%d%c", -1, " 
    "[i == n]);
            return;
        }
        int beg = 1;
        while(a[beg] <= a[beg + 1])
            ++beg;
        if(a[beg] > a[beg + 1])
            ++beg;
        for(int i = beg, u = 1; u <= n; ++i, ++u)
            printf("%d%c", a[i], " 
    "[u == n]);
        cnt = 0;
        int cur = beg;
        while(cur - beg + 1 <= n) {
            l[++cnt] = cur, r[cnt] = cur;
            while(a[r[cnt]] <= a[r[cnt] + 1])
                ++r[cnt];
            for(int i = l[cnt]; i <= r[cnt]; ++i)
                b[i] = cnt;
            cur = r[cnt] + 1;
        }
        for(int i = beg, u = 1; u <= n; ++i, ++u)
            printf("%d%c", b[i], " 
    "[u == n]);
    }
    

    组划分好了,从全局最高的开始向右一直找,找到第一个小于它的一半的,经过上面的筛选可知这样的数一定存在,那么这个全局最高的值就确定了。然后把它的整个组全部从右往左依次更新。更新完这组后,尝试打通这一组和上一组的联系。

    但是好复杂耶!

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int, int> pii;
    #define rep(i,p1,p2) for(int i=(p1);i<=(p2);++i)
    #define per(i,p1,p2) for(int i=(p1);i>=(p2);--i)
    
    const int INF = 0x3f3f3f3f;
    const int MOD = 1e9 + 7;
    
    int qpow(ll x, ll n) {
        ll res = 1;
        while(n) {
            if(n & 1)
                res = res * x % MOD;
            x = x * x % MOD;
            n >>= 1;
        }
        return res;
    }
    
    const int MAXN = 2e5;
    
    /*---*/
    
    int n;
    int a[200005], b[200005], c[200005];
    int l[200005], r[200005], cnt;
    
    void test_case() {
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i) {
            scanf("%d", &a[i]);
            a[i + n] = a[i];
        }
        a[2 * n + 1] = a[1];
        int maxai = 0, minai = INF;
        for(int i = 1; i <= n; ++i) {
            if(a[i] < minai)
                minai = a[i];
            if(a[i] > maxai)
                maxai = a[i];
        }
        if(maxai <= 2 * minai) {
            for(int i = 1; i <= n; ++i)
                printf("%d%c", -1, " 
    "[i == n]);
            return;
        }
        int beg = 1;
        while(a[beg] <= a[beg + 1])
            ++beg;
        if(a[beg] > a[beg + 1])
            ++beg;
        for(int i = beg, u = 1; u <= n; ++i, ++u)
            printf("%d%c", a[i], " 
    "[u == n]);
        cnt = 0;
        int cur = beg;
        while(cur - beg + 1 <= n) {
            l[++cnt] = cur, r[cnt] = cur;
            while(a[r[cnt]] <= a[r[cnt] + 1])
                ++r[cnt];
            for(int i = l[cnt]; i <= r[cnt]; ++i)
                b[i] = cnt;
            cur = r[cnt] + 1;
        }
        for(int i = beg, u = 1; u <= n; ++i, ++u) {
            if(i > n)
                b[i - n] = b[i];
            if(i + n <= 2 * n)
                b[i + n] = b[i];
            printf("%d%c", b[i], " 
    "[u == n]);
        }
        int maxaii = beg;
        for(; a[maxaii] != maxai; ++maxaii)
            ;
        {
            int cnt = 1;
            for(int i = maxaii + 1; maxai <= 2 * a[i]; ++i)
                ++cnt;
            c[maxaii] = cnt;
            int cur = maxaii - 1, head = maxaii, rest = n - 1, curmin = maxaii;
            while(rest) {
                while(rest && b[cur] == b[head]) {
                    c[cur] = ++cnt;
                    curmin = min(curmin, a[cur]);
                    --cur;
                    if(cur == 0)
                        cur = n;
                    --rest;
                }
                if(rest) {
                    if(a[cur] <= 2 * curmin) {
                        c[cur] = ++cnt;
                        curmin = min(curmin, a[cur]);
                        head = cur;
                        --cur;
                        if(cur == 0)
                            cur = n;
                        --rest;
                    } else {
                        c[cur] = cnt = 1;
                        curmin = a[cur];
                        head = cur;
                        --cur;
                        if(cur == 0)
                            cur = n;
                        --rest;
                    }
                }
            }
        }
        for(int i = maxaii; i > n; --i)
            c[i - n] = c[i];
        for(int i = 1; i <= n; ++i)
            printf("%d%c", c[i], " 
    "[i == n]);
    }
    
    10
    4 3 6 4 2 4 2 4 1 9
    

    O(n)的做法:再看一下觉得像双指针,从任意一个位置开始向右扩展,到极限之后不断向左更新,当无法再向左更新时,说明右端点实在太小了,这个时候就把右端点吐出去,直到区间中的最小值大于等于左端点的一半的下整。数据结构,就弄个双栈队列查最小值就可以了。

    
    int n, a[100005], b[100005];
    struct Queue {
        static const int MAXN = 1000000;
        static const int INF = 1061109567;
        int s1[MAXN + 5], s2[MAXN + 5];
        int s1top, s2top, s1min;
    
        void Clear() {
            s1top = 0;
            s2top = 0;
            s2[0] = INF;
            s1min = INF;
        }
    
        void Push(int x) {
            s1[++s1top] = x;
            s1min = min(s1min, x);
        }
    
        void Pop() {
            if(s2top)
                --s2top;
            else {
                while(s1top)
                    s2[++s2top] = min(s2[s2top - 1], s1[s1top--]);
                --s2top;
                s1min = INF;
            }
        }
    
        int Size() {
            return s1top + s2top;
        }
    
        int Min() {
            return min(s2[s2top], s1min);
        }
    } Q;
    
    void test_case() {
        Q.Clear();
        scanf("%d", &n);
        int maxi = 1, mini = 1;
        for(int i = 1; i <= n; ++i) {
            scanf("%d", &a[i]);
            if(a[i] > a[maxi])
                maxi = i;
            if(a[i] < a[mini])
                mini = i;
        }
        a[n + 1] = a[1];
        if(a[maxi] <= 2 * a[mini]) {
            for(int i = 1; i <= n; ++i)
                printf("%d%c", -1, " 
    "[i == n]);
            return;
        }
        int cur = maxi;
        while(a[maxi] <= 2 * a[cur]) {
            ++cur;
            if(cur == n + 1)
                cur = 1;
        }
        --cur;
        if(cur == 0)
            cur = n;
        while(cur != maxi) {
            Q.Push(a[cur]);
            --cur;
            if(cur == 0)
                cur = n;
        };
        int rest = n;
        while(rest) {
            while(a[cur] > 2 * Q.Min())
                Q.Pop();
            Q.Push(a[cur]);
            b[cur] = Q.Size();
            --cur;
            if(cur == 0)
                cur = n;
            --rest;
        }
        for(int i = 1; i <= n; ++i)
            printf("%d%c", b[i], " 
    "[i == n]);
        return;
    }
    

    假算法:假如是变成RMQ,就是对每个起点作为左端点,找一个最长的k,使得这个区间的最大值<=2*这个区间的最小值,变成一个二分然后在ST表上面找的问题,这个实现最简单,写这个。(这个做法好假啊,写出来才发现)

  • 相关阅读:
    1010每次备份我的MySQL数据库
    1008win7与虚拟机中的linux共享文件的(详细)方法
    0930MySQL中实现高性能高并发计数器方案(例如文章点击数)
    0929shell操作mysql
    0929mysql前缀索引如何找到合适的位数
    0929mysql 用户管理和权限设置
    学习笔记之机器学习实战 (Machine Learning in Action)
    学习笔记之Python for Data Analysis
    学习笔记之入行数据科学,这些书一定要看
    面试总结之Python
  • 原文地址:https://www.cnblogs.com/KisekiPurin2019/p/12053936.html
Copyright © 2020-2023  润新知