• Codeforces Round #602 (Div. 2, based on Technocup 2020 Elimination Round 3)


    A - Math Problem

    题意:给n条线段[l,r],求再加一条可以退化成点的线段,与所有线段各至少有一个公共点。

    题解:求出最右边的左端点和最左边的右端点,左端点>右端点,则说明你的这个线段要把这两个连起来,否则你的线段就是现在的[左端点,右端点]上的随便一个点。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
     
    void test_case() {
        int n;
        scanf("%d", &n);
        int L = 1, R = 1e9;
        for(int i = 1; i <= n; ++i) {
            int l, r;
            scanf("%d%d", &l, &r);
            L = max(L, l);
            R = min(R, r);
        }
        int ans = 0;
        if(L > R)
            ans = L - R;
        printf("%d
    ", ans);
    }
     
    int main() {
    #ifdef KisekiPurin
        freopen("KisekiPurin.in", "r", stdin);
    #endif // KisekiPurin
        int t = 1;
        scanf("%d", &t);
        for(int ti = 1; ti <= t; ++ti) {
            //printf("Case #%d: ", ti);
            test_case();
        }
    }
    

    B - Box

    题意:给一个前缀最大值序列,求满足这个序列的任意一个排列。

    题解:生成的是满足这个序列的字典序最大的一个排列。而实际上因为这个序列是递增的,怎么乱构造都可以。首先每次最大值变化的位置肯定放这个罪魁祸首,然后扫一遍之后可能会有一些位置没有放,这个时候lower_bound其最大值得到大于等于其最大值的任意一个值。然后--之后就是小于它的最大的一个值,把它放过去就可以了。或者每次就放begin迭代器,直到begin迭代器超过最大值就-1,这样是字典序最小。

    注:事实上并没有用到set的性质,所以应该是可以使用链表(但是链表不能用lower_bound然后--,或者说维护起来巨恶心)、并查集(并查集实现的伪单向链表)来实现的。并查集实现的双向链表比真正的双向链表好多了,不容易翻车。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
     
    int q[100005];
    int p[100005];
    set<int> tmp;
     
    void test_case() {
        int n;
        scanf("%d", &n);
        tmp.clear();
        for(int i = 1; i <= n; ++i) {
            p[i] = -1;
            tmp.insert(i);
        }
        for(int i = 1; i <= n; ++i)
            scanf("%d", &q[i]);
        p[1] = q[1];
        tmp.erase(q[1]);
        for(int i = 2; i <= n; ++i) {
            if(q[i] > q[i - 1]) {
                p[i] = q[i];
                tmp.erase(q[i]);
            }
        }
        for(int i = 1; i <= n; ++i) {
            if(p[i] == -1) {
                auto t = tmp.lower_bound(q[i]);
                if(t == tmp.begin()) {
                    puts("-1");
                    return;
                }
                --t;
                p[i] = *t;
                tmp.erase(t);
            }
        }
        for(int i = 1; i <= n; ++i)
            printf("%d%c", p[i], " 
    "[i == n]);
    }
     
    int main() {
    #ifdef KisekiPurin
        freopen("KisekiPurin.in", "r", stdin);
    #endif // KisekiPurin
        int t = 1;
        scanf("%d", &t);
        for(int ti = 1; ti <= t; ++ti) {
            //printf("Case #%d: ", ti);
            test_case();
        }
    }
    
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    int p[100005];
    int q[100005];
    
    struct PseudoPrevLinkedList {
        int n;
        bool vis[100005];
        int prev[100005];
    
        void Init(int _n) {
            n = _n;
            for(int i = 1; i <= n; ++i) {
                vis[i] = 0;
                prev[i] = i - 1;
            }
        }
    
        int FindPrev(int x) {
            int r = prev[x];
            while(vis[r])
                r = prev[r];
            int t;
            while(prev[x] != r) {
                t = prev[x];
                prev[x] = r;
                x = t;
            }
            return r;
        }
    
        void Remove(int x) {
            vis[x] = 1;
        }
    } fl;
    
    void test_case() {
        int n;
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i) {
            p[i] = -1;
            scanf("%d", &q[i]);
        }
        fl.Init(n);
        p[1] = q[1];
        fl.Remove(p[1]);
        for(int i = 2; i <= n; ++i) {
            if(q[i] > q[i - 1]) {
                p[i] = q[i];
                fl.Remove(p[i]);
            }
        }
        for(int i = 1; i <= n; ++i) {
            if(p[i] == -1) {
                int res = fl.FindPrev(q[i]);
                if(!res) {
                    puts("-1");
                    return;
                }
                p[i] = res;
                fl.Remove(p[i]);
            }
        }
        for(int i = 1; i <= n; ++i)
            printf("%d%c", p[i], " 
    "[i == n]);
    }
    
    int main() {
    #ifdef KisekiPurin
        freopen("KisekiPurin.in", "r", stdin);
    #endif // KisekiPurin
        int t = 1;
        scanf("%d", &t);
        for(int ti = 1; ti <= t; ++ti) {
            //printf("Case #%d: ", ti);
            test_case();
        }
    }
    

    C - Messy

    题意:给一个"("")"括号相等的长度不超过2000的括号串,要求每次反转一个子区间,最后使得括号串合法,且经过零点恰好k次。

    题解:贪心,每次贪心把前两个字符变成"("")",这样最后就有n/2次零点,然后把开头开始的一小段破坏掉就可以了(假如需要的话,不要画蛇添足),至多用n/2+1次。因为网络的原因没有交上去,问题不大,实力到了自然会收敛。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    int n, k;
    string s;
    vector<pair<int, int> > ans;
    
    void test_case() {
        ans.clear();
        cin >> n >> k >> s;
        int curp = 1;
        while(s.length()) {
            if(s[0] == '(' && s[1] == ')') {
                s = s.substr(2, s.length());
                curp += 2;
            } else if(s[0] == ')' && s[1] == '(') {
                ans.push_back({curp + 0, curp + 1});
                s = s.substr(2, s.length());
                curp += 2;
            } else if(s[0] == ')') {
                int j = 2;
                while(s[j] == s[0])
                    ++j;
                ans.push_back({curp + 0, curp + j});
                string s1 = s.substr(0, j + 1);
                string s2 = s.substr(j + 1, s.length());
                reverse(s1.begin(), s1.end());
                s = s1 + s2;
                s = s.substr(2, s.length());
                curp += 2;
            } else {
                int j = 2;
                while(s[j] == s[0])
                    ++j;
                ans.push_back({curp + 1, curp + j});
                string s0 = s.substr(0, 1);
                string s1 = s.substr(1, j);
                string s2 = s.substr(j + 1, s.length());
                reverse(s1.begin(), s1.end());
                s = s0 + s1 + s2;
                s = s.substr(2, s.length());
                curp += 2;
            }
        }
        int tk = n / 2 - k;
        if(1 + 2 * tk >= 2)
            ans.push_back({2, 1 + 2 * tk});
        cout << ans.size() << endl;
        for(int i = 0; i < ans.size(); ++i)
            cout << ans[i].first << " " << ans[i].second << endl;
    }
    
    int main() {
    #ifdef KisekiPurin
        freopen("KisekiPurin.in", "r", stdin);
    #endif // KisekiPurin
        int t = 1;
        scanf("%d", &t);
        for(int ti = 1; ti <= t; ++ti) {
            //printf("Case #%d: ", ti);
            test_case();
        }
    }
    

    注:要注意string的substr方法的使用,真的烦死人,substr(a,b)是从a位置开始截取最多b个字符形成的子串。其实假如用静态字符数组会不会方便一些?要是自己写一个String类就可以使用自己的全新方法了。

    D1 - Optimal Subsequences (Easy Version)

    范围小到100,随便搞,用可持久化Treap/主席树就很简单了,直接在线回答,可惜空间消耗巨大。

    D2 - Optimal Subsequences (Hard Version)

    题意:给一个n个元素(<=2e5)序列,定义一个长度为k的最优子序列,当其和最大,且满足和最大的条件下字典序最小。每次询问一个k,pos,询问第k个子序列的第pos位置是哪个数。

    题解:显然,和最大就是贪心,然后字典序最小的意思就是同大的优先贪前面的,所以按照题目意思排序。离线所有询问然后排序,然后每次往平衡树里面插新的元素直到变成k长的序列,然后找出每个pos位置是谁。然后就觉得很逗了,这个不是直接线段树就完了?具体来说就是线段树上二分,线段树上保存siz,然后利用siz的信息来找当前树上的第pos大,这个操作应该是去主席树那里抄一个就好了。

    注:使用可持久化TreapRE了,然后改了一下上限之后WA了,所以还是对可持久化Treap的板子不熟,对空间消耗没有一个合理认识(假如你开512MB会这样?)。然后发现离线回答之后每次回答最短的k,然后就可以破坏掉了,不需要可持久化。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
     
    struct FHQTreap {
    #define ls(p) ch[p][0]
    #define rs(p) ch[p][1]
        static const int MAXN = 200000 + 5;
        int val1[MAXN], val2[MAXN], ch[MAXN][2], rnd[MAXN], siz[MAXN], tot, root;
     
        void Init() {
            tot = root = 0;
        }
     
        void PushUp(int p) {
            siz[p] = siz[ls(p)] + siz[rs(p)] + 1;
        }
     
        void SplitValue(int p, int v, int &x, int &y) {
            if(!p) {
                x = y = 0;
                return;
            }
            if(v < val1[p]) {
                y = p;
                SplitValue(ls(p), v, x, ls(p));
                PushUp(y);
            } else {
                x = p;
                SplitValue(rs(p), v, rs(p), y);
                PushUp(x);
            }
        }
     
        void SplitRank(int p, int rk, int &x, int &y) {
            if(!p) {
                x = y = 0;
                return;
            }
            if(rk <= siz[ls(p)]) {
                y = p;
                SplitRank(ls(p), rk, x, ls(p));
                PushUp(y);
            } else {
                x = p;
                SplitRank(rs(p), rk - siz[ls(p)] - 1, rs(p), y);
                PushUp(x);
            }
        }
     
        int Merge(int x, int y) {
            if(!x || !y)
                return x | y;
            if(rnd[x] < rnd[y]) {
                rs(x) = Merge(rs(x), y);
                PushUp(x);
                return x;
            } else {
                ls(y) = Merge(x, ls(y));
                PushUp(y);
                return y;
            }
        }
     
        int NewNode(int v, int v2) {
            ++tot;
            ch[tot][0] = ch[tot][1] = 0;
            val1[tot] = v, val2[tot] = v2, rnd[tot] = rand();
            siz[tot] = 1;
            return tot;
        }
     
        void Insert(int &root, int v, int v2) {
            int x = 0, y = 0;
            SplitValue(root, v, x, y);
            root = Merge(Merge(x, NewNode(v, v2)), y);
        }
     
        void Remove(int &root, int v) {
            int x = 0, y = 0, z = 0;
            SplitValue(root, v, x, z);
            SplitValue(x, v - 1, x, y);
            y = Merge(ls(y), rs(y));
            root = Merge(Merge(x, y), z);
        }
     
        int GetRank(int &root, int v) {
            int x = 0, y = 0;
            SplitValue(root, v - 1, x, y);
            int rk = siz[x] + 1;
            root = Merge(x, y);
            return rk;
        }
     
        int GetValue2(int &root, int rk) {
            int x = 0, y = 0, z = 0;
            SplitRank(root, rk, x, z);
            SplitRank(x, rk - 1, x, y);
            int v2 = val2[y];
            root = Merge(Merge(x, y), z);
            return v2;
        }
    #undef ls(p)
    #undef rs(p)
    } ft;
     
    struct Node {
        int val;
        int pos;
        Node(int v = 0, int p = 0) {
            val = v;
            pos = p;
        }
        bool operator<(const Node &node)const {
            if(val != node.val)
                return val > node.val;
            return pos < node.pos;
        }
    } node[200005];
     
    struct Query {
        int id, k, pos, ans;
    } q[200005];
     
    bool cmp1(const Query &q1, const Query &q2) {
        if(q1.k != q2.k)
            return q1.k < q2.k;
        return q1.pos < q2.pos;
    }
     
    bool cmp2(const Query &q1, const Query &q2) {
        return q1.id < q2.id;
    }
     
    void InputQuery(int n) {
        for(int i = 1; i <= n; ++i) {
            scanf("%d%d", &q[i].k, &q[i].pos);
            q[i].id = i;
        }
        sort(q + 1, q + 1 + n, cmp1);
    }
     
    void OutputQuery(int n) {
        sort(q + 1, q + 1 + n, cmp2);
    //    for(int i = 1; i <= n; ++i)
    //        printf("%d%c", q[i].ans, " 
    "[i == n]);
        for(int i = 1; i <= n; ++i)
            printf("%d
    ", q[i].ans);
    }
     
    void test_case() {
        int n;
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i) {
            scanf("%d", &node[i].val);
            node[i].pos = i;
        }
        sort(node + 1, node + 1 + n);
        int m;
        scanf("%d", &m);
        InputQuery(m);
        ft.Init();
        int j = 1;
        for(int i = 1; i <= m; ++i) {
            while(ft.siz[ft.root] < q[i].k) {
                ft.Insert(ft.root, node[j].pos, node[j].val);
                ++j;
            }
            q[i].ans = ft.GetValue2(ft.root, q[i].pos);
        }
        OutputQuery(m);
    }
     
    int main() {
    #ifdef KisekiPurin
        freopen("KisekiPurin.in", "r", stdin);
    #endif // KisekiPurin
        int t = 1;
        //scanf("%d", &t);
        for(int ti = 1; ti <= t; ++ti) {
            //printf("Case #%d: ", ti);
            test_case();
        }
    }
    

    E - 暂缺

    F1 - Wrong Answer on test 233 (Easy Version)

    题意:一套选择题,有n道题,每道题k个选项,选对得1分,选错得0分。求有多少种答案,使得其循环右移一次之后得分比之前严格高,模998244353。

    题解:看起来很像dp,但是怎么搞?

  • 相关阅读:
    Git 基本操作
    Git 基础
    MarkDown教程
    Python常用函数
    Python生成器
    Python列表生成式
    Python迭代
    Python切片
    Python函数
    Python不可变对象
  • 原文地址:https://www.cnblogs.com/KisekiPurin2019/p/11924913.html
Copyright © 2020-2023  润新知