zoukankan      html  css  js  c++  java
  • Codeforces Round #343 (Div. 2)

    题目链接:https://codeforces.com/contest/629

    说起来经过一个月分数不升反降,道理很简单,前面起飞的是数据结构和数论场,假如连续整点图论就一直降落了。没事多积累一下知道在进步。

    A - Far Relative’s Birthday Cake

    水题,随便组合一下。

    B - Far Relative’s Problem

    水题,不过题意有点混乱,搞点差分就行。

    *C - Famil Door and Brackets

    题意:给一个长度为 (m(1leq m leq n)) 的括号串 (s) ,在它前面和后面加上可以为空的前缀 (p) 和后缀 (q) ,使其结果为恰好为长度为 (n(1leq n leq 2cdot 10^5)) 的合法括号串。限制 (n-mleq2000)

    题解:由于这个限制,所以可以用个 (O(n^2)) 的算法。先统计出整个 (s) 串的左右差和最小左右差。然后就可以设计一些转移。

    不过越界真的好烦耶!

    (dp1[i][j]) 为前缀放了i个,左右差为j的方案数,由必须合法可知j>=0。

    (dp2[i][j]) 为前缀和后缀共放了i个,左右差为j的方案数,由必须合法可知j>=0。

    由必须合法可知,从前缀加上 (s) 后要求结果仍>=0,故必须从 (jgeq minsum) 的前缀,转移到 (j+sum) 的后缀,且这里不能够越界!

    int n, m;
    char s[100005];
    ll dp1[2005][2005];
    ll dp2[2005][2005];
    
    void test_case() {
        int n, m;
        scanf("%d%d%s", &n, &m, s + 1);
        int sum = 0, minsum = 0;
        for(int i = 1; i <= m; ++i) {
            if(s[i] == '(')
                ++sum;
            else {
                --sum;
                minsum = min(minsum, sum);
            }
        }
        dp1[0][0] = 1;
        int c = 2000;
        for(int i = 1; i <= c; ++i) {
            for(int j = 0; j <= c; ++j) {
                dp1[i][j] += (j > 0 ? dp1[i - 1][j - 1] : 0) + dp1[i - 1][j + 1];
                if(dp1[i][j] >= MOD)
                    dp1[i][j] %= MOD;
            }
        }
    
    //    for(int i = 0; i <= c; ++i) {
    //        for(int j = 0; j <= c; ++j)
    //            printf("dp1[%d][%d]=%lld
    ", i, j, dp1[i][j]);
    //        puts("");
    //    }
    //    puts("---");
    
        for(int j = -minsum; j + sum <= c && j <= c; ++j) {
            for(int k = 0; k <= c; ++k)
                dp2[k][j + sum] += dp1[k][j];
        }
    
    //    for(int i = 0; i <= c; ++i) {
    //        for(int j = 0; j <= c; ++j)
    //            printf("dp2[%d][%d]=%lld
    ", i, j, dp2[i][j]);
    //        puts("");
    //    }
    //    puts("---");
    
        for(int i = 1; i <= c; ++i) {
            for(int j = 0; j <= c; ++j) {
                dp2[i][j] += (j > 0 ? dp2[i - 1][j - 1] : 0) + dp2[i - 1][j + 1];
                if(dp2[i][j] >= MOD)
                    dp2[i][j] %= MOD;
            }
        }
    
    //    for(int i = 0; i <= c; ++i) {
    //        for(int j = 0; j <= c; ++j)
    //            printf("dp2[%d][%d]=%lld
    ", i, j, dp2[i][j]);
    //        puts("");
    //    }
    //    puts("---");
    
        printf("%lld
    ", dp2[n - m][0] % MOD);
    }
    

    *D - Babaei and Birthday Cake

    粗看像单调队列优化dp什么的,仔细分析。

    题意:有 (n(1leq nleq 10^5)) 个圆柱体蛋糕,其中第 (i) 个蛋糕要么放在桌子上,要么放在一个 (j<i)(V_j<V_i) 的蛋糕上。求最大体积的蛋糕。

    题解:意思是什么面积和高都是没用的,全部算体积。看起来也只和最后一个蛋糕有关。

    (dp[i]) 表示以第 (i) 个蛋糕结尾的最大体积,则: (dp[i]=V_i+maxlimits_{j=0; and ; V_j<V_i}^{i-1}dp[j]) ,要求小于某个key的value的最小值。这不是平衡树吗?仔细想想权值线段树也可做。

    注意到其实可以把圆周率提取出来,所以是准确运算。而且相同的V值,后面的结果肯定不会比前面更差。(所以修改的时候直接赋值而不是取max)

    const double eps = 1e-8;
    const double PI = acos(-1.0);
    
    struct SegmentTree {
    #define ls (o<<1)
    #define rs (o<<1|1)
        static const int MAXN = 100000;
        ll mx[(MAXN << 2) + 5];
    
        void PushUp(int o) {
            mx[o] = max(mx[ls], mx[rs]);
        }
    
        void Build(int o, int l, int r) {
            if(l == r)
                mx[o] = 0;
            else {
                int m = l + r >> 1;
                Build(ls, l, m);
                Build(rs, m + 1, r);
                PushUp(o);
            }
        }
    
        void Update(int o, int l, int r, int p, ll v) {
            if(l == r) {
                mx[o] = v;
                return;
            } else {
                int m = l + r >> 1;
                if(p <= m)
                    Update(ls, l, m, p, v);
                if(p >= m + 1)
                    Update(rs, m + 1, r, p, v);
                PushUp(o);
            }
        }
    
        ll Query(int o, int l, int r, int ql, int qr) {
            if(ql <= l && r <= qr) {
                return mx[o];
            } else {
                int m = l + r >> 1;
                ll res = 0;
                if(ql <= m)
                    res = Query(ls, l, m, ql, qr);
                if(qr >= m + 1)
                    res = max(res, Query(rs, m + 1, r, ql, qr));
                return res;
            }
        }
    #undef ls
    #undef rs
    } st;
    
    int n;
    ll V[100005];
    ll v[100005];
    int id[100005];
    
    void test_case() {
        int n;
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i) {
            ll r, h;
            scanf("%lld%lld", &r, &h);
            V[i] = v[i] = r * r * h;
        }
        sort(v + 1, v + 1 + n);
        int c = unique(v + 1, v + 1 + n) - (v + 1);
        for(int i = 1; i <= n; ++i)
            id[i] = lower_bound(v + 1, v + 1 + c, V[i]) - v;
        st.Build(1, 1, c);
        for(int i = 1; i <= n; ++i) {
            if(id[i] == 1) {
                st.Update(1, 1, c, 1, V[i]);
                continue;
            } else {
                ll res = st.Query(1, 1, c, 1, id[i] - 1);
                st.Update(1, 1, c, id[i], V[i] + res);
                continue;
            }
        }
        printf("%.12f
    ", PI * st.Query(1, 1, c, 1, c));
    }
    

    收获:检查线段树的下标,不要写错线段树的下标!

  • 相关阅读:
    MySQL教程详解之存储引擎介绍及默认引擎设置
    最简单MySQL教程详解(基础篇)之多表联合查询
    Postfix常用命令和邮件队列管理(queue)
    备份数据库
    Docker基本命令
    ASCII码表
    mysql基本了解
    顺序对列,环形队列,反向链式栈
    进制的标识符
    多个线程的时间同步
  • 原文地址:https://www.cnblogs.com/KisekiPurin2019/p/12292918.html
Copyright © 2011-2022 走看看