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

    这次题目挺有意思的,可惜D题差几分钟没交对。E题的博弈还是很基础的SG函数,让我复习了下博弈。

    A - Uncowed Forces

    基本的输入输出,一个公式输出来即可,但是要注意的是,写之前先化简变成全为整数的情况。

    #include <iostream>
    #include <stdio.h>
    using namespace std;
    int m[10],w[10];
    
    int main(int argc, const char * argv[]) {
        for(int i=0;i<5;i++) cin>>m[i];
        for(int i=0;i<5;i++) cin>>w[i];
        int hs,hu;
        cin>>hs>>hu;
        int ans;
        ans=0;
        for(int i=0;i<5;i++)
        {
            ans +=  max(150*(i+1),(i+1)*(500-2*m[i])-50*w[i]);
        }
        ans+=100*hs;
        ans-=hu*50;
        cout<<ans;
        return 0;
    }
    View Code

    B - More Cowbell

    这题其实还是要想想的,比赛的时候想了下加猜了下。

    给一个递增序列长度为n,然后有k个箱子,每个箱子最多只能放两个数。最后求箱子里面最大的不超过多少。

    1.箱子肯定尽量全部用上。

    2.如果n>k的话,则有n-k个箱子放两个数,2*k-n个箱子放一个数,则把最大2*k-n个数单个放入箱子中

    3.剩下来的2*(n-k)个小的数,按一大一小的选择方式放入箱子中。(为什么呢? 其实仔细想想肯定是这样贪心的,顺序只要变了结果不可能会变小)

    需要注意的是:第2步中最大的肯定为整个序列最大的一个数,第3步最大的必须一个一个枚举。

    #include <iostream>
    #include <stdio.h>
    using namespace std;
    
    int g[100100];
    
    int main(int argc, const char * argv[]) {
        int n,k;
        cin>>n>>k;
        for(int i=1;i<=n;i++) cin>>g[i];
        if(k>=n) cout<<g[n];
        else
        {
            int tn=2*(n-k);
            int ans=max(g[n],g[1]+g[tn]);
            for(int i=1;i<=(n-k);i++)
                ans=max(ans,g[i]+g[tn+1-i]);
            cout<<ans;
            
        }
        return 0;
    }
    View Code

    C - Alternative Thinking

    这题基本上就在考智商吧,感觉聪明人瞬间就可以看到解法了,我等菜B推了快半小时才看出来。。。

    首先,有一个很重要的性质,对于一段0,1序列,翻转后的01间断个数不变。

    比如0111001011,等价于010101,翻转后变为101010,间断个数不变。

    第二个重要性质,一次翻转最多只能加2

    然后就是最关键的:我把00,11这样连续相同的段且长度=2的叫做连段。比如0011101中00,11(靠左)和11(靠右)就是连段,

    如果序列中出现了两个以上的连段. 如xxx00yyy11xxx,只需要变成xxx01y‘y’y‘01xxx,有之前说的性质1,yyy的间断个数不变,所以整体就加2

    如果序列中只有一个连段,那么只能加1

    如果没有连段,那么序列就是0和1相间,只能加0

    这样只需要O(n)统计一遍连段的个数即可。

    #include <iostream>
    #include <stdio.h>
    using namespace std;
    
    char g[100100];
    
    int main(int argc, const char * argv[]) {
        int n;
        cin>>n;
        scanf("%s",g);
        int ans=1;
        char pre=g[0];
        for(int i=1;i<n;i++)
        {
            if(g[i]!=pre)
            {
                ans++;
                pre=g[i];
            }
        }
        //然后看有没有三个的
        int flag3=0;
        for(int i=0;i<n-2;i++)
        {
            if(g[i]==g[i+1]&&g[i+1]==g[i+2])
            {
                flag3=1;
                break;
            }
        }
        if(flag3==1)
        {
            printf("%d",ans+2);
        }
        else
        {
            int flag2=0;
            for(int i=0;i<n-1;i++)
            {
                if(g[i]==g[i+1]) flag2++;
            }
            if(flag2>=2) printf("%d",ans+2);
            else
            {
                if(flag2==1) printf("%d",ans+1);
                else printf("%d
    ",ans);
            }
        }
        return 0;
    }
    View Code

    D - Moodular Arithmetic

    这题一开始我还以为用到了数论和组合数学的知识,其实好像用到了一点置换的概念,但是还是一个推理就能解出来的题目。

     

    观察这个函数。

    1.如果k=0,那么f(0)=0,只有这个信息,然后f(1),f(2),...,f(p-1)都可以随便选,结果就是p^(p-1)

    2.k!=0时

    设有一个x1,则f(kx1 mod p) = k*f(x1) mod p

    设x2=x1*k%p,则f(kx2 mod p) = k*f(x2) mod p

    ...

    设xs=xs-1*k%p,且xs=x1 (因为一定会有循环结)

    f(x1) = ks*f(x1) mod p

    因为p是素数,如果kmod p=1,则f(x1)可以为任意选,也就是有p种可能

    如果 ks mod p != 1,f(x1)只能为0,也就是只有一种可能。

    这样一来,对于在一个循环结内的数就能知道有多少可能。把所有循环结的情况相乘就是最后的答案了。

    #include <iostream>
    #include <stdio.h>
    using namespace std;
    #define MOD 1000000007
    int g[1001000];
    int mark[1001000];
    int main(int argc, const char * argv[]) {
        int p,k;
        cin>>p>>k;
        if(k==0)
        {
            long long ans=1;
            for(int i=1;i<p;i++)
                ans=ans*p%MOD;
            cout<<ans;
            return 0;
        }
        for(int i=0;i<p;i++)
        {
            g[i] = ((long long)i*k)%p;
        }
        long long ans=1;
        for(int i=0;i<p;i++)
        {
            if(mark[i]) continue;
            mark[i]=1;
            long long tmp=1;
            int ti = g[i];
            while(ti!=i)
            {
                mark[ti]=1;
                tmp++;
                ti=g[ti];
            }
            long long tk=1;
            for(int j=0;j<tmp;j++)
                tk=(tk*k)%p;
            if(tk==1) ans=ans*p%MOD;
            else ans*=1;
        }
        cout<<ans;
        return 0;
    }
    View Code

    E - Lieges of Legendre

    我在开始想这题的时候,忘了sg函数,想凭找规律找到结果。 结果我想多了,对于一半的情况是可以找规律的,但是另一半我是始终没找出来,我想如果聪明人应该还是可以通过规律做出这题。通过这题我也知道挂不得区域赛什么的,博弈题很少,因为博弈题套路固定,而且基本上就那几种特殊的博弈。巴什,威佐夫,nim。难的估计太难大家又不会了。o(╯□╰)o

    像我一样,忘记sg函数的或者还不会的可以看这里:博弈SG函数

     知道了SG函数后,这题其实只需要分k为奇偶的情况先定义好f(1),f(2),f(3)。然后就是普通求SG函数的套路,因为数据量达到10^9,所以有些情况可以优化下。
     
  • 相关阅读:
    LeetCode 189. Rotate Array
    LeetCode 965. Univalued Binary Tree
    LeetCode 111. Minimum Depth of Binary Tree
    LeetCode 104. Maximum Depth of Binary Tree
    Windows下MySQL的安装与配置
    LeetCode 58. Length of Last Word
    LeetCode 41. First Missing Positive
    LeetCode 283. Move Zeroes
    《蚂蚁金服11.11:支付宝和蚂蚁花呗的技术架构及实践》读后感
    删除docker下的镜像
  • 原文地址:https://www.cnblogs.com/chenhuan001/p/5032004.html
Copyright © 2011-2022 走看看