• Codeforces Round #574 (Div. 2)


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

    好像做过这一场,这场的CDE都比较有印象,特别是E,随便掏个数据结构就可以做了。

    A - Drinks Choosing

    题意:每份饮料恰好有2支,有 (n) 个人 (n) 种饮料,每个人有自己最喜欢的一种饮料,要恰好买 (lceilfrac{n}{2} ceil) 份饮料,求最多有多少人喝到自己最喜欢的饮料?

    题解:某种饮料每满2就肯定买一份,最后肯定剩下一群1或者0,随便买一些1。

    int cnt[1005];
    
    void test_case() {
        int n, k;
        scanf("%d%d", &n, &k);
        for(int i = 1; i <= n; ++i) {
            int x;
            scanf("%d", &x);
            ++cnt[x];
        }
        int cnt2 = 0;
        for(int i = 1; i <= k; ++i)
            cnt2 += cnt[i] / 2;
    
        printf("%d
    ", (n + 1) / 2 + cnt2);
    }
    

    B - Sport Mafia

    题意:有 (n) 次操作,其中若是eat操作则会使得糖果数量 -1 ,若是第i次put操作则会使得糖果数量 +i ,已知最后恰好剩下 (k) 颗糖果,保证有唯一解,求eat了多少次。

    题解:显然eat越多就会越少,满足单调性可以二分。

    其实设put了x次,则有:

    (frac{x(x+1)}{2}-(n-x)=k)

    (x^2+x-2n+2x=2k)

    (x^2+3x+(-2n-2k)=0)

    由韦达定理可知两根一正一负,可以直接解这个方程的正根。

    int n, k;
    bool check(int e) {
        ll p = n - e;
        ll sump = (1 + p) * p / 2;
        return (sump - e) >= k;
    }
     
    void test_case() {
        scanf("%d%d", &n, &k);
        int L = 0, R = n;
        while(1) {
            int M = (L + R) >> 1;
            if(L == M) {
                if(check(R)) {
                    printf("%d
    ", R);
                    return;
                }
                assert(check(L));
                printf("%d
    ", L);
                return;
            }
            if(check(M))
                L = M;
            else
                R = M - 1;
        }
    }
    

    C - Basketball Exercise

    题意:有两行正数,要从两行之间交替选,求选出的和的最大值。

    题解:直接dp就可以。设 (dp[i][0]) 表示恰好以第 (i) 个位置的第 (0) 行结尾的最大值,那么有个显然的转移方程在程序中。

    int a[100005][2];
    ll dp[100005][2];
    
    void test_case() {
        int n;
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i)
            scanf("%d", &a[i][0]);
        for(int i = 1; i <= n; ++i)
            scanf("%d", &a[i][1]);
        dp[1][0] = a[1][0];
        dp[1][1] = a[1][1];
        for(int i = 2; i <= n; ++i) {
            dp[i][0] = max(dp[i - 1][1], dp[i - 2][1]) + a[i][0];
            dp[i][1] = max(dp[i - 1][0], dp[i - 2][0]) + a[i][1];
        }
        printf("%lld
    ", max(dp[n][0], dp[n][1]));
    }
    

    D1 - Submarine in the Rybinsk Sea (easy edition)

    见下

    *D2 - Submarine in the Rybinsk Sea (hard edition)

    题意:定义一个合成函数 (f(a,b)) 它的合成规则是先把 (b) 的个位放在个位,再把 (a) 的个位放在十位,依次交替直到其中一个放完,剩下的就全放剩下那个数的剩余的高位。给 (n) 个数,求 (sumlimits_{i=1}^{n}sumlimits_{j=1}^{n}f(a_i,a_j))

    题解:对于某个 (x) 而言,所有相同长度的 (y)(x) 进行 (f(x,y)) 合成之后, (x) 对结果的贡献是固定的(可以通过处理 (x) 的各位在结果的哪位中确定)。所以可以统计各种长度的数量,再算贡献。好像这题在前几天也写过一个很像的。

    int n;
    ll a[100005];
    ll sumL[100005][11];
    ll sumR[100005][11];
    
    int cntlen[11];
    
    ll tmp[11];
    ll pow10[21];
    
    void calcL(int i) {
        ll x = a[i];
        for(int j = 1; j <= 10; ++j) {
            tmp[j] = x % 10;
            x /= 10;
            //printf("%lld", tmp[j]);
        }
        //printf("
    ");
        for(int Rlen = 1; Rlen <= 10; ++Rlen) {
            sumL[i][Rlen] = 0;
            for(int j = 1; j <= Rlen; ++j)
                sumL[i][Rlen] += pow10[2 * j - 1] * tmp[j];
            for(int j = Rlen + 1; j <= 10; ++j)
                sumL[i][Rlen] += pow10[j + Rlen - 1] * tmp[j];
            sumL[i][Rlen] %= MOD;
            //printf("a[%d]=%lld sumLRlen[%d]=%lld
    ", i, a[i], Rlen, sumL[i][Rlen]);
        }
        //puts("");
    }
    
    void calcR(int i) {
        ll x = a[i];
        for(int j = 1; j <= 10; ++j) {
            tmp[j] = x % 10;
            x /= 10;
            //printf("%lld", tmp[j]);
        }
        //printf("
    ");
        for(int Llen = 1; Llen <= 10; ++Llen) {
            sumR[i][Llen] = 0;
            for(int j = 1; j <= Llen; ++j)
                sumR[i][Llen] += pow10[2 * j - 1 - 1] * tmp[j];
            for(int j = Llen + 1; j <= 10; ++j)
                sumR[i][Llen] += pow10[j + Llen - 1] * tmp[j];
            sumR[i][Llen] %= MOD;
            //printf("a[%d]=%lld sumRLlen[%d]=%lld
    ", i, a[i], Llen, sumR[i][Llen]);
        }
        //puts("");
    }
    
    void calcLen(int i) {
        int len = 0;
        ll x = a[i];
        while(x) {
            ++len;
            x /= 10;
        }
        //printf("i=%d len=%d
    ", i, len);
        ++cntlen[len];
    }
    
    void test_case() {
        pow10[0] = 1;
        for(int i = 1; i <= 20; ++i)
            pow10[i] = pow10[i - 1] * 10 % MOD;
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i)
            scanf("%lld", &a[i]);
        for(int i = 1; i <= n; ++i) {
            calcL(i);
            calcR(i);
            calcLen(i);
        }
        ll sum = 0;
        for(int i = 1; i <= n; ++i) {
            for(int Rlen = 1; Rlen <= 10; ++Rlen)
                sum += sumL[i][Rlen] * cntlen[Rlen] % MOD;
            for(int Llen = 1; Llen <= 10; ++Llen)
                sum += sumR[i][Llen] * cntlen[Llen] % MOD;
            sum %= MOD;
        }
        printf("%lld
    ", sum);
    }
    

    E - OpenStreetMap

    题意:给一个 (n*m(1leq n,m leq 3000)) 的矩阵,然后有一个 (a*b(1leq a leq n, 1leq b leq m)) 框,这个框正好遍历整个矩阵,求框中的最小值的和。

    题解:用单调队列或者双栈队列,每列开一个这样的队列。逐行扫描,每次每列的队列Q中都存当前的 (a) 行的元素,那么转移的时候就可以直接把这些元素的最小值取出来了。扫描完一行之后命令所有的每列的队列Q都向下移动一个位置。

    struct Queue {
        static const int MAXN = 3005;
        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[3005], RQ;
    
    int g[3005 * 3005];
    int h[3005][3005];
    
    void test_case() {
        int n, m, a, b;
        scanf("%d%d%d%d", &n, &m, &a, &b);
        int g0, x, y, z;
        scanf("%d%d%d%d", &g0, &x, &y, &z);
        g[0] = g0;
        for(int i = 1; i <= n * m; ++i)
            g[i] = (1ll * g[i - 1] * x + y) % z;
        int top = 0;
        for(int i = 1; i <= n; ++i) {
            for(int j = 1; j <= m; ++j) {
                h[i][j] = g[top++];
                //printf("%d%c", h[i][j], " 
    "[j == m]);
            }
        }
        for(int j = 1; j <= m; ++j) {
            Q[j].Clear();
            for(int i = 1; i <= a; ++i)
                Q[j].Push(h[i][j]);
        }
        ll sum = 0;
        for(int i = a; i <= n; ++i) {
            RQ.Clear();
            for(int j = 1; j <= b; ++j)
                RQ.Push(Q[j].Min());
            for(int j = b; j <= m; ++j) {
                sum += RQ.Min();
                RQ.Pop();
                if(j + 1 <= m)
                    RQ.Push(Q[j + 1].Min());
            }
            if(i + 1 <= n) {
                for(int j = 1; j <= m; ++j) {
                    Q[j].Pop();
                    Q[j].Push(h[i + 1][j]);
                }
            }
        }
        printf("%lld
    ", sum);
    }
    
  • 相关阅读:
    Git -- 分支管理简介
    Git -- 从远程库克隆
    Git -- 添加远程仓库
    C# sha256 加密算法
    如何将IOS版本的更新下载文件指向到自己的服务器
    如何让windows服务器IIS支持.apk/.ipa文件下载
    vistual studio 去除 git 源代码 绑定
    Redis 环境搭建与使用(C#)
    c#图片添加水印
    C#使用WSDL服务总结
  • 原文地址:https://www.cnblogs.com/KisekiPurin2019/p/12256262.html
Copyright © 2020-2023  润新知