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,所以有些情况可以优化下。
     
  • 相关阅读:
    微信公众平台开发教程(一) 微信公众账号注册流程
    DNS----域名解析系统
    C#编程总结(九)字符编码
    向大神学习
    C# 正则表达式
    js 正则表达式 取反
    H5 打开App
    Fiddler 过滤器的使用
    Fiddler 默认不能抓取页面信息的问题
    js 元素Dom新建并插入页面createElement
  • 原文地址:https://www.cnblogs.com/chenhuan001/p/5032004.html
Copyright © 2011-2022 走看看