• Codeforces Round #603 (Div. 2)


    A - Sweet Problem

    题意:有3种糖果(什么蜡烛?分不清candy和candle的一定不止我一个中国人),每天要吃两种不同的各1颗。给出3种的数量,求可以吃多少天。

    题解:假如数据小的话直接每次去最高的两个--,然后sort就行了。可惜搞不得。但是每次取最高这个思路是没错的。假如三种数量很平均,那么肯定是加起来除以2的下整,不对的情况在于最多的糖不能充分利用,临界情况是恰好相等,所以:

    void test_case() {
        int a[4];
        for(int i = 1; i <= 3; ++i)
            scanf("%d", &a[i]);
        sort(a + 1, a + 1 + 3);
        a[3] = min(a[3], a[1] + a[2]);
        printf("%d
    ", (a[1] + a[2] + a[3]) / 2);
    }
    

    B - PIN Codes

    题意:有n(<=10)种PIN码,PIN码就是4位可前导零的数字,每次可以改动一位数字,求使得所有PIN码两两不同的最小改法。

    题解:因为一共就10种码,刚好有10种数字,所以无论如何都最多改1位就够了。每次取重复的两个出来,然后遍历其中的数字,枚举把它改成谁会和大家都不一样。注意最后还要输出前后对应的关系。

    map<string, int> m;
    string a[15];
    
    void test_case() {
        m.clear();
        int n;
        cin >> n;
        for(int i = 1; i <= n; ++i) {
            cin >> a[i];
            m[a[i]]++;
        }
        int ans = 0;
        while(m.size() < n) {
            string id;
            for(auto i : m) {
                if(i.second >= 2) {
                    id = i.first;
                    break;
                }
            }
            ++ans;
            for(int j = 0; j <= 9; ++j) {
                if(id[0] == ('0' + j))
                    continue;
                string tmp = id;
                tmp[0] = char('0' + j);
                if(m.count(tmp))
                    continue;
                else {
                    m[tmp]++;
                    m[id]--;
                    for(int k = 1; k <= n; ++k) {
                        if(a[k] == id) {
                            a[k] = tmp;
                            break;
                        }
                    }
                    break;
                }
            }
    
        }
        cout << ans << endl;
        for(int i = 1; i <= n; ++i)
            cout << a[i] << endl;
    }
    

    C - Everyone is a Winner!

    题意:数论分块

    注意:数论分块的复杂度是 (lceil2sqrt{n} ceil) ,搞不清楚就弄个vec或者开大很多或者输出出来看一下。还有就是1e9的根号是3.2e4。

    int a[64005];
    
    void test_case() {
        int n, atop = 0;
        scanf("%d", &n);
        for(int l = 1, r; l <= n; l = r + 1) {
            r = n / (n / l);
            a[++atop] = n / l;
        }
        a[++atop] = 0;
        reverse(a + 1, a + 1 + atop);
        printf("%d
    ", atop);
        for(int i = 1; i <= atop; ++i)
            printf("%d%c", a[i], " 
    "[i == atop]);
    }
    

    D - Secret Passwords

    题意:两个字符串等价,当其中含有至少一个相同的字母时,并且可以传递。例如"ab"和"bc"等价,"bc"和"cd"等价,那么在本题中"ab"="bc"="cd"。求有多少种不等价的字符串。

    题解:传递性,并查集,开26个节点表示26种字母,字符串就是无向边,全部等价连在一起,数有多少个连通块,记得去掉题目中没有出现的字母。

    struct DisjointSetUnion {
        static const int MAXN = 30;
        int n, fa[MAXN + 5], rnk[MAXN + 5];
     
        int cnt;
     
        void Init(int _n) {
            n = _n;
            for(int i = 1; i <= n; i++) {
                fa[i] = i;
                rnk[i] = 1;
            }
            cnt = n;
        }
     
        int Find(int u) {
            int r = fa[u];
            while(fa[r] != r)
                r = fa[r];
            int t;
            while(fa[u] != r) {
                t = fa[u];
                fa[u] = r;
                u = t;
            }
            return r;
        }
     
        bool Merge(int u, int v) {
            u = Find(u), v = Find(v);
            if(u == v)
                return false;
            else {
                if(rnk[u] < rnk[v])
                    swap(u, v);
                fa[v] = u;
                rnk[u] += rnk[v];
                --cnt;
                return true;
            }
        }
    } dsu;
     
    char s[1000005];
    bool vis[30 + 5];
    bool vis2[30 + 5];
     
    void test_case() {
        int n;
        scanf("%d", &n);
        memset(vis2, 0, sizeof(vis2));
        dsu.Init(26);
        for(int i = 1; i <= n; ++i) {
            scanf("%s", s);
            int len = strlen(s);
            memset(vis, 0, sizeof(vis));
            for(int j = 0; j < len; ++j) {
                vis[s[j] - 'a' + 1] = 1;
                vis2[s[j] - 'a' + 1] = 1;
            }
            int first = -1;
            for(int j = 1; j <= 26; ++j) {
                if(vis[j]) {
                    if(first == -1)
                        first = j;
                    dsu.Merge(first, j);
                }
            }
        }
        int sum = 0;
        for(int i = 1; i <= 26; ++i)
            sum += 1 - vis2[i];
        printf("%d
    ", dsu.cnt - sum);
    }
    

    E - Editor

    题意:一个文本编辑器,有一段文本和一个光标,LR左右移动光标,其他字母修改文本。编辑器要给嵌套的括号染色,求最少的用色。

    题解:一开始还在往FHQTreap那里想。其实合法的括号序列只需要:最终左右平衡且任何情况非负。嵌套最多时显然是最大值。用线段树就可以,树状数组感觉很不自然。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    struct SegmentTree {
    #define ls (o<<1)
    #define rs (o<<1|1)
        static const int MAXN = 1000000;
        static const int INF = 0x3f3f3f3f;
        int mi[(MAXN << 2) + 5];
        int ma[(MAXN << 2) + 5];
        int lz[(MAXN << 2) + 5];
    
        void PushUp(int o) {
            mi[o] = min(mi[ls], mi[rs]);
            ma[o] = max(ma[ls], ma[rs]);
        }
    
        void PushDown(int o, int l, int r) {
            if(lz[o]) {
                lz[ls] += lz[o];
                lz[rs] += lz[o];
                //int m = l + r >> 1;
                mi[ls] += lz[o];
                mi[rs] += lz[o];
                ma[ls] += lz[o];
                ma[rs] += lz[o];
                lz[o] = 0;
            }
        }
    
        void Build(int o, int l, int r) {
            if(l == r) {
                mi[o] = 0;
                ma[o] = 0;
            } else {
                int m = l + r >> 1;
                Build(ls, l, m);
                Build(rs, m + 1, r);
                PushUp(o);
            }
            lz[o] = 0;
        }
    
        void Update(int o, int l, int r, int ql, int qr, int v) {
            if(ql <= l && r <= qr) {
                lz[o] += v;
                mi[o] += v;
                ma[o] += v;
            } else {
                PushDown(o, l, r);
                int m = l + r >> 1;
                if(ql <= m)
                    Update(ls, l, m, ql, qr, v);
                if(qr >= m + 1)
                    Update(rs, m + 1, r, ql, qr, v);
                PushUp(o);
            }
        }
    
        int QueryMin(int o, int l, int r, int ql, int qr) {
            if(ql <= l && r <= qr) {
                return mi[o];
            } else {
                PushDown(o, l, r);
                int m = l + r >> 1;
                int res = INF;
                if(ql <= m)
                    res = QueryMin(ls, l, m, ql, qr);
                if(qr >= m + 1)
                    res = min(res, QueryMin(rs, m + 1, r, ql, qr));
                return res;
            }
        }
    
        int QueryMax(int o, int l, int r, int ql, int qr) {
            if(ql <= l && r <= qr) {
                return ma[o];
            } else {
                PushDown(o, l, r);
                int m = l + r >> 1;
                int res = -INF;
                if(ql <= m)
                    res = QueryMax(ls, l, m, ql, qr);
                if(qr >= m + 1)
                    res = max(res, QueryMax(rs, m + 1, r, ql, qr));
                return res;
            }
        }
    #undef ls
    #undef rs
    } st;
    
    int n, a[1000005];
    char op[1000005];
    
    void test_case() {
        scanf("%d%s", &n, op + 1);
        //st.Build(1, 1, n);
        //memset(a, 0, sizeof(a));
        int cur = 1, sum = 0;
        for(int i = 1; i <= n; ++i) {
            if(op[i] == 'L') {
                --cur;
                if(cur == 0)
                    cur = 1;
            } else if(op[i] == 'R') {
                ++cur;
            } else {
                int tmp = 0;
                if(op[i] == '(')
                    tmp = 1;
                else if(op[i] == ')')
                    tmp = -1;
                int d = tmp - a[cur];
                if(d != 0) {
                    a[cur] = tmp;
                    sum += d;
                    st.Update(1, 1, n, cur, n, d);
                }
            }
            int ans = -1;
            if(sum == 0 && st.QueryMin(1, 1, n, 1, n) >= 0)
                ans = st.QueryMax(1, 1, n, 1, n);
            printf("%d%c", ans, " 
    "[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();
        }
    }
    
    /*
        1. 小数据问题退化:
            输入为0或1会不会有特殊情况?其他的比如最小环要有三个点,强连通分量缩到最后一个点等等。
        2. 内存:
            内存的空间有没有开够?有时有反向边,有时有额外新加的边。线段树开了4倍了吗?
            可持久化数据结构会不会内存溢出?多组数据时vector会不会翻车?字符大小写有考虑吗?
            多组数据有进行初始化吗?memset会不会翻车?
        3. 算术溢出:
            乘法上溢出了?忘记取模了?输入输出用了%d?让无符号数越到负数了?
        4. 习惯:
            函数都有指定返回值吗?返回值非void函数的每一个分支都有显式的返回值吗?确定不会进入的分支可以assert一把。
            Yes和No有没有反,大小写有没有错?有没有搞错n和m?离散化之后的cn有没有错?换行和空格要怎么整?priority要把符号反过来。
        5. 其他bug:
            基环树是树加环,不是链加环。
    */
    

    注:这道题还有 (O(n)) 的做法,维护两个对顶栈,光标右移时把右边栈的元素吸到左边,光标左移时反过来。众所周知栈可以维护前缀最大值以及前缀最小值。但是实际操作起来速度还不如线段树,因为线段树并不是每次都Update,只有真正Update的时候才是log时间的,移动光标的速度飞快,无效改值的速度飞快,平均意义下来也很快。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    struct Stack {
        static const int MAXN = 1000000;
        static const int INF = 0x3f3f3f3f;
        int s[MAXN + 5];
        int mi[MAXN + 5];
        int ma[MAXN + 5];
        int top, sum;
    
        void Clear() {
            top = 0;
            s[top] = 0;
            mi[top] = INF;
            ma[top] = -INF;
        }
    
        void Push(int v) {
            ++top;
            s[top] = v;
            sum += s[top];
            mi[top] = min(mi[top - 1], sum);
            ma[top] = max(ma[top - 1], sum);
        }
    
        void Pop() {
            if(top) {
                sum -= s[top];
                --top;
            }
        }
    
        int Top() {
            return s[top];
        }
    
        int Min() {
            return mi[top];
        }
    
        int Max() {
            return ma[top];
        }
    } ;
    
    struct Editor {
        Stack LStack, RStack;
        int cur, sum;
    
        void LeftShift() {
            if(cur == 1)
                return;
            RStack.Push(-LStack.Top());
            LStack.Pop();
            --cur;
        }
    
        void RightShift() {
            LStack.Push(-RStack.Top());
            RStack.Pop();
            ++cur;
        }
    
        void Clear() {
            LStack.Clear();
            RStack.Clear();
            sum = 0;
            RightShift();
        }
    
        void Update(int v) {
            int pv = LStack.Top();
            if(pv == v)
                return;
            LStack.Pop();
            sum -= pv;
            LStack.Push(v);
            sum += v;
        }
    
        int Min() {
            return min(LStack.Min(), RStack.Min());
        }
    
        int Max() {
            return max(LStack.Max(), RStack.Max());
        }
    
        void Show() {
            for(int i = 1; i <= LStack.top; ++i)
                printf(" %d", LStack.s[i]);
            printf(" |");
            for(int i = RStack.top; i >= 1; --i)
                printf(" %d", RStack.s[i]);
            printf("
    ");
        }
    
    } editor;
    
    char op[1000005];
    
    void test_case() {
        int n;
        scanf("%d%s", &n, op + 1);
        editor.Clear();
        for(int i = 1; i <= n; ++i) {
            if(op[i] == 'L') {
                editor.LeftShift();
            } else if(op[i] == 'R') {
                editor.RightShift();
            } else {
                int tmp = 0;
                if(op[i] == '(')
                    tmp = 1;
                else if(op[i] == ')')
                    tmp = -1;
                editor.Update(tmp);
            }
            //editor.Show();
            int ans = -1;
            if(editor.sum == 0 && editor.Min() >= 0)
                ans = editor.Max();
            printf("%d%c", ans, " 
    "[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();
        }
    }
    
    /*
        1. 小数据问题退化:
            输入为0或1会不会有特殊情况?其他的比如最小环要有三个点,强连通分量缩到最后一个点等等。
        2. 内存:
            内存的空间有没有开够?有时有反向边,有时有额外新加的边。线段树开了4倍了吗?
            可持久化数据结构会不会内存溢出?多组数据时vector会不会翻车?字符大小写有考虑吗?
            多组数据有进行初始化吗?memset会不会翻车?
        3. 算术溢出:
            乘法上溢出了?忘记取模了?输入输出用了%d?让无符号数越到负数了?
        4. 习惯:
            函数都有指定返回值吗?返回值非void函数的每一个分支都有显式的返回值吗?确定不会进入的分支可以assert一把。
            Yes和No有没有反,大小写有没有错?有没有搞错n和m?离散化之后的cn有没有错?换行和空格要怎么整?priority要把符号反过来。
        5. 其他bug:
            基环树是树加环,不是链加环。
    */
    

    注意:这道题的左移操作要特判左边是不是没有元素了。每个栈的最底部存放这个操作的幺元,这样就不用判空了。

    这道题貌似也可以用平衡树来维护,只不过这样子使用的平衡树就和线段树一样了,常数还大。

  • 相关阅读:
    R语言修改vector、matrix、dataframe列名
    R语言获取数据框的行数
    R语言的which函数,针对没有符合条件的返回值为integer(0),之后如何判断
    ArrayList总结
    经常涉及到的技术
    今天开始写博客啦!(测试..)
    [摘转] JS 数组合并问题
    t_category
    在 MySql 的 update 语句中使用 case when else end
    JSP 取当前时间
  • 原文地址:https://www.cnblogs.com/KisekiPurin2019/p/11961192.html
Copyright © 2020-2023  润新知