【博客】2019-12-15第十八次CCF xzc ABCE

[ 2019-12-15第十八次CCF计算机软件能力认证]总结


导言:今天第一次参加CCF考试,考完回来迫不及待地想要做一点笔记


链接:我做的CCF题目汇总Apare_xzc <--


比赛题目(凭记忆描述)

A.报数

题面:在这里插入图片描述

Sample Input 1

20

Sample Output 1

2
1
1
0

Explanation of Sample 1

报数过程为:

甲:1,乙:2,丙:3,丁:4
甲:5,乙:6,丙:跳过,丁:8
甲:9,乙:10,丙:11,丁:12
甲:13,乙:跳过,丙:15,丁:16
甲:跳过,乙:18,:19,丁:20
甲:跳过,乙:22,丙:23,丁:24
在丁报出24后,四个人总计报出了20个数,游戏结束。

Sample Input 2

66

Sample Output 2

7
5
11
5

大致题意:

  有甲乙丙丁四个人从1开始循环报数报数。甲1,乙2,丙3,丁4,甲5,乙6,丙7(跳过),丁8,甲9 ……若某个人的数是7的倍数或者某一位含有数字7,那么这个人跳过(不报出声音)。给定以个n(n<=1000),表示报出了n个数,问甲,乙,丙,丁四人各自跳过了多少次?

思路:

  直接模拟即可

我的代码

//Apare_xzc 2019.12.15 A题
#include <bits/stdc++.h>
using namespace std;
#define LL long long
int a[10];
bool jump(int x) //判断是否是7的倍数或者含有7
{
    if(x%7==0) return true;
    while(x>0)
    {    
        if(x%10==7) return true;
        x/=10;
    }
    return false;
}
int main()
{
    int n;cin>>n; 
    int k = 1; //k代表当前报数人的编号,甲、乙、丙、丁依次为1,2,3,4
    int tot = 0;
    for(int i=1;tot<n;++i) //tot==n时结束
    {
        if(jump(i)) a[k]++; //跳过的话就给当前的人技术
        else tot++;
        if(++k==5) k = 1; //循环报数
    }
    for(int i=1;i<=4;++i) //输出结果
        cout<<a[i]<<endl;

    return 0;
}

B.回收站选址</font### 题面在这里插入图片描述)在这里插入图片描述

Sample Input 1

7
1 2
2 1
0 0
1 1
1 0
2 0
0 1

Sample Output 1

0
0
1
0
0

Explanation of Sample 1

在这里插入图片描述
如图所示,仅有(1,1)可选为回收站地址,评分为2.

Sample Input 2

2
0 0
-100000 10

Sample Output 2

0
0
0
0
0

Explanation of Sample 2

不存在可选地址。

Sample Input 3

11
9  10
10 10
11 10
12 10
13 10
11 9
11 8
12 9
10 9
10 11
12 11

Sample Output 3

0
2
1
0
0

Explanation of Sample 3

1分选址:(10,10)和(12,10);
2分选址:(11,9)

数据范围

测试点1和2,保证对于任意的i皆满足0<=xi, yi<=2;
测试点3、4和5,保证对于任意的i皆满足0<=xi, yi<= 500;
测试点6、7和8,保证对于任意的i皆满足0<=xi, yi<= 10^9;
测试点9和10,保证对于任意的i皆满足|xi|, |yi|<=10^9,即坐标可以是负数。
所有的测试点保证1<=n<=10^3。

大致题意:

  根据航拍信息,得到了n个比较理想的位置可能可以建立垃圾站。每个位置坐标为xi,yi( xi , yi 均为整数)。某个位置要建垃圾站,要满足它的上( x-1 , y )、下(x+1 , y)、左( x , y-1)、右(x,y+1)都比较理想。现在给可以建垃圾站的地方打分。可建垃圾站的地方,左上(x-1,y-1),左下(x+1,y-1),右上(x-1,y+1),右下(x+1,y+1)4个位置理想位置的个数等于这个位置的评分。 输出5行,分别输出评分为0,1,2,3,4的位置的个数

思路:

  小模拟?按照题意模拟一遍即可,定义一个结构体类型或者直接用pairl<int,int>来记录位置,判断每个位置上下左右是否理想,满足的话再计分即可。可以用map来保存所有理想的位置坐标

我的代码

#include <bits/stdc++.h>
using namespace std;
#define LL long long
struct Node{  //记录位置信息
    int x,y; //x为横坐标,y为纵坐标
    Node(int _x=0,int _y=0):x(_x),y(_y){} //构造函数
    bool operator < (const Node& rhs)const{
        return x==rhs.x?y<rhs.y:x<rhs.x; //重载小于号(要用map)
    }
};
map<Node,int> mp; //记录地图
int cnt[10]; //cnt[i]表示得分为i的地址的个数
int dx[] = { 1, 1,-1,-1}; //偏移量
int dy[] = { 1,-1, 1,-1};
void solve(Node node) //判断某个点是否可以建垃圾场
{
    int x = node.x, y = node.y; //上下左右都理想才可
    if(!mp[Node(x,y+1)]||!mp[Node(x,y-1)]||!mp[Node(x+1,y)]||!mp[Node(x-1,y)])
        return;
    int mark = 0; //分数
    for(int i=0;i<4;++i)
    {
        int xx = x+dx[i],yy=y+dy[i];
        if(mp[Node(xx,yy)]) mark++;
    }
    cnt[mark]++; //计数
    return;    
}
int main()
{
    vector<Node> v;
    int n,x,y;
    cin>>n;

    for(int i=0;i<n;++i)
    {
        scanf("%d%d",&x,&y);
        v.push_back(Node(x,y));    
        mp[v[i]] = 1;  //标记该点在是理想点
    }
    for(int i=0;i<n;++i)
    {
        solve(v[i]);
    }
    for(int i=0;i<=4;++i) //输入答案
        cout<<cnt[i]<<endl;

    return 0;
}

C. 化学方程式

题面:在这里插入图片描述)在这里插入图片描述

数据范围

1<=n<=100
输入的化学方程式都是符合题目中给出的定义的,且长度不超过1000
系数不会有前导零,也不会有为零的系数化学方程式的任何一边,其中任何一种元素的原子总个数都不超过10^9

在这里插入图片描述

题意:

  输入T,之后T个无空格的字符串,如果已经配平,输出“Y”,否则输出“N”,一共输出T行
  化学方程式就是我们高中学的方程式,它有一个规范的词法定义,大致如下(凭借记忆+我的理解):
方程:: 表达式=表达式
表达式:: 式子|表达式+式子
式子:: 分子式|系数+分子式
分子式:: 块|块+分子式
块::(块)|块+数字|(块)+数字
数字::1|2|3|4|5|6|7|8|9|数字+(0|1|2...|9)
块::化学元素|(化学元素)
化学元素::大写字母|大写字母+小写字母
一定不规范,大致意思理解就行了

  • 如果分子式前面没系数,默认系数为1,如H2, 16H2
  • 分子式中有括号,而且括号可以嵌套,如Na(Au(CN)2)
  • 化学元素只有2种形式,一种是单个大写字母,如H, O, S, C, N ……另一种是1个大写字母+1个小写字母,如 Cl, Ag, Au, Cu, Ba, Fe, Al ……
  • 方程中没有空格

Sample Input

(单击右上角可复制到剪切板)

11
H2+O2=H2O
2H2+O2=2H2O
H2+Cl2=2NaCl
H2+Cl2=2HCl
CH4+2O2=CO2+2H2O
CaCl2+2AgNO3=Ca(NO3)2+2AgCl
3Ba(OH)2+2H3PO4=6H2O+Ba3(PO4)2
3Ba(OH)2+2H3PO4=Ba3(PO4)2+6H2O
4Zn+10HNO3=4Zn(NO3)2+NH4NO3+3H2O
4Au+8NaCN+2H2O+O2=4Na(Au(CN)2)+4NaOH
Cu+As=CS+Au

Sample Output

N
Y
N
Y
Y
Y
Y
Y
Y
Y
N

我的思路:

  • 检查配平,思路很清晰,就是看方程两边每种元素的原子个数是否相等
  • 我们首先找到等号的位置,把方程分割为左右两端
  • 然后我们找到每个加号的位置,就可以用‘+’ 分割出若干的项(系数+分子式or分子式)
  • 然后我们处理每一个项,统计项里面==每种元素(原子)的个数==(最重要的地方)
  • 我们可以用map记录每种原子的个数
  • 统计每种元素的个数,重要的有三个点:
  1. 注意每一项前面统一的系数(配平的系数),如 ==32==H2O
  2. 注意分子式中每种元素的系数,如H==3==PO==4==
  3. 注意括号以及括号的嵌套,如 ==Na(Au(CN)2)==,这个可以用栈来解决
  • 如何用栈来解决呢? 这个我觉得类似中缀表达式转后缀表达式,和括号有关,我们如果遇到左括号'==(==',直接入栈,如果遇到原子式,讲原子式和其个数一起入栈,如果遇到右括号'==)==', 讲栈中所有原子式pop出来到临时栈里面(为了保证顺序不乱),直到遇见第一个左括号'(',然后把pop出来的原子式的个数乘以右括号后面的系数,再push回去

我的代码

#include <bits/stdc++.h>
using namespace std;
#define LL long long
char str[10000];
int posEqual,lenStr;
vector<int> vLeftAdd; //the pos of '+'
vector<int> vRightAdd;
map<string,int> mp; //element:=cnt 统计每个原子的个数 
map<string,int>::iterator it;
struct Node{  
    string exp; //原子式(化学元素) 
    int countt; //原子的个数 
    Node(){}
    Node(string s,int c){
        exp = s; countt = c;
    }
};
void calOne(string s,int flag); //用来统计一项的原子个数 
//flag==1 Left;  flag==-1 Right;
bool judge();
int main()
{
//    freopen("in.txt","r",stdin);
    int T;cin>>T;
    while(T--)
    {
        scanf("%s",str); //缓冲区存放方程式 
        vLeftAdd.clear();
        vRightAdd.clear();
        mp.clear(); //初始化 
        vLeftAdd.push_back(-1);
        lenStr = strlen(str); //方程式的长度 
        bool onLeft = true; 
        for(int i=0;i<lenStr;++i) //统计等号的位置,以及等号左右'+'的位置 
        {
            if(str[i]=='=') posEqual = i,onLeft=false;
            else if(str[i]=='+')
            {
                if(onLeft) vLeftAdd.push_back(i); 
                //等号左边'+'的下标存在vleftAdd中 
                else vRightAdd.push_back(i);
                //等号右边'+'的下标存在vRightAdd中 
            }
        }
        if(judge()) //已经配平 
        {
            printf("Y\n");
        }
        else 
        {
            printf("N\n");
        }    
    }
    return 0;
}
void calOne(string s,int flag) //flag==1 Left;  flag==-1 Right;
{
    int i = 0;
    int Num = 0;
    int len = s.length();
    if(!isdigit(s[0]))
    {
        Num = 1;
    }
    else
    {
        for(i=0;isdigit(s[i]);++i)
        {
            Num = Num*10+s[i]-'0';
        }
    }
    //i is the first pos of the substance;
    stack<Node> St; //stack to take element
    for(int j=i;j<len;++j)
    {
        char now = s[j];
        if(now=='(') 
        {
            St.push(Node(string("("),1));
            continue;
        }
        else if(now==')')
        {
            int totProduct = 0;
            if(j==len-1||!isdigit(s[j+1]))
            {
                totProduct = 1;
            }
            else
            {
                int k;
                for(k=j+1;isdigit(s[k]);++k)
                {
                    totProduct = totProduct*10+s[k]-'0';
                }
                j = k-1;
            }
            stack<Node> tmpv;
            while(!St.empty())
            {
                Node tpS = St.top();
                St.pop();
                if(tpS.exp==string("("))
                {

                    break;
                }
                else
                {
                    tmpv.push(Node(tpS.exp,tpS.countt*totProduct));
                }
            }
            while(!tmpv.empty())
            {
                St.push(tmpv.top());
                tmpv.pop();
            }
            continue;
        }
        else if(isupper(now))
        {
            string ttmmpp = string("");
            if(j==len-1||!islower(s[j+1])) //only one letter
            {
                ttmmpp += now;
                int cntWord = 0;
                if(j==len-1||!isdigit(s[j+1]))
                {
                    cntWord = 1;
                }
                else 
                {
                    int jj;
                    for(jj=j+1;isdigit(s[jj]);++jj)
                    {
                        cntWord = cntWord*10+s[jj]-'0';
                    }
                    j = jj-1;
                }
                St.push(Node(ttmmpp,cntWord));
                continue;
            }
            else if(j<len&&islower(s[j+1])) //word has two letter: upper+lower
            {
                string tmptmp = "";
                tmptmp+=now;
                tmptmp+=s[j+1];
                j++;
                int cntWord = 0;
                if(j==len-1||!isdigit(s[j+1]))
                {
                    cntWord = 1;
                }
                else 
                {
                    int jj;
                    for(jj=j+1;isdigit(s[jj]);++jj)
                    {
                        cntWord = cntWord*10+s[jj]-'0';
                    }
                    j = jj-1;
                }
                St.push(Node(tmptmp,cntWord));
                continue;
            }
        }
    }
    Node nodd;
    while(!St.empty())
    {
        nodd = St.top();
        St.pop();
        int updateN = Num*nodd.countt*flag;
        mp[nodd.exp] += updateN;
    }
}
bool judge()
{
    int szL = vLeftAdd.size();
    vector<int> pHead;
    for(int i=0;i<szL;++i)
    {
        str[vLeftAdd[i]] = '\0';
        pHead.push_back(vLeftAdd[i]+1);
    }
    str[posEqual] = '\0';
    for(int i=0;i<(int)pHead.size();++i)
    {
        string tmp = str+pHead[i];
        calOne(tmp,1);
    }
    pHead.clear();
    int szR = vRightAdd.size();
    pHead.push_back(posEqual+1);
    for(int i=0;i<szR;++i)
    {
        str[vRightAdd[i]] = '\0';
        pHead.push_back(vRightAdd[i]+1);
    }
    for(int i=0;i<(int)pHead.size();++i)
    {
        string tmp = str+pHead[i];
        calOne(tmp,-1);
    }
    for(it=mp.begin();it!=mp.end();++it)
    {
        if(it->second) return false;
    }
    return true;
}

D题没看,E题按题意写了暴力,不知道能骗多少分^_^


E.魔数

大致题意:

  • 我们先定义6个常数:
    U0 = 314882150829468584
    U1 = 427197303358170108
    U2 = 1022292690726729920
    U3 = 1698479428772363217
    U4 = 2006101093849356424
    Md = 2009731336725594113

  • 再定义一个函数:
    f(x) = (x%mod)%2019

  • 现在给定一个数列a,长度为n,其中a[i] = i
    然后给出m个操作,每个操作输入一个Li,Ri(1<=Li<=Ri<=n)
    令sum = f(a[Li])+f(a[Li+1])+f(a[Li+2]+...+f(a[Ri-1])+f(a[Ri]))
    要求输出sum
    然后令p = sum%5
    然后进行一个操作:将a[Li],a[Li+1],a[Li+2]...a[Ri-1],a[Ri]都乘以Up

  • 数据范围:(n<=100,000, m<=1000,000)*

    样例

    (输入是考试的时候往文件里存了拷回来的,输出是根据输入用Python暴力计算的,应该没问题)

    Sample Input 1
    4 4 
    1 3
    3 4
    3 3 
    1 3
    Sample Output1
    6
    1105
    1735
    4744
    Sample Input 2
    100 100
    45 74
    38 50
    7 45
    42 62
    83 100
    50 51
    8 11
    93 98
    64 70
    15 87
    30 87
    13 79
    14 81
    18 79
    70 88
    25 39
    13 57
    55 85
    80 92
    83 90
    54 75
    1 61
    17 42
    25 49
    39 77
    32 45
    83 87
    30 47
    59 84
    25 50
    1 82
    21 45
    72 96
    3 85
    16 64
    52 92
    28 29
    84 88
    26 93
    10 67
    27 76
    57 62
    43 69
    63 66
    5 59
    9 46
    49 53
    35 50
    3 19
    23 62
    38 73
    17 68
    34 83
    42 91
    13 92
    19 62
    17 70
    18 75
    95 99
    35 90
    81 91
    59 63
    5 90
    22 87
    51 88
    25 61
    56 91
    50 78
    11 60
    11 18
    27 45
    57 82
    16 54
    3 94
    33 56
    9 71
    68 88
    24 36
    7 64
    48 85
    58 76
    20 43
    9 90
    24 27
    71 97
    25 95
    73 97
    55 83
    22 43
    53 55
    68 88
    12 44
    25 87
    14 46
    34 56
    15 35
    7 80
    46 87
    23 71
    8 93
    Sample Output2
    1785
    5741
    10423
    24915
    1647
    2154
    1872
    8559
    12936
    60048
    52916
    79974
    61897
    50541
    16819
    15646
    48044
    30156
    14581
    6906
    17346
    45805
    25217
    29837
    44539
    12602
    5964
    16894
    23972
    30665
    64047
    28029
    26086
    89745
    49102
    40236
    2297
    6040
    64456
    57625
    48151
    8311
    27574
    4166
    52887
    37703
    4922
    17603
    17729
    35771
    35915
    52458
    54055
    44140
    70298
    39690
    49407
    48808
    4775
    55131
    9378
    2839
    75644
    58663
    40660
    29344
    38759
    31862
    51760
    7924
    22539
    22003
    31095
    86980
    25718
    61094
    18995
    13703
    56434
    35626
    18678
    22776
    82576
    3233
    24072
    76470
    23887
    28161
    26150
    2017
    19769
    31420
    63547
    31533
    24513
    20199
    62729
    39286
    43276
    80411

    我的思路

      暴力模拟。每次只需要求f(ai),不关心ai的大小。f(x) = x%mod%2019, 所以我们只需要维护ai%mod即可;每次暴力更新:
    a[i] = a[i] * u[sum%5] % mod 但是2个1E18的数会乘爆,我们要用==快速乘==(俄罗斯农民乘法)

    这样的话应该小数据都能过了

    我的代码

    (输出正确是没问题的,就是m,n大了肯定会TLE,估计AC的话要用线段树之类的维护吧)

    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <cstring>
    #include <string>
    #include <vector>
    #include <map>
    #include <set>
    #include <vector>
    #include <stack>
    #include <queue>
    using namespace std;
    #define LL unsigned long long
    const LL U0 = 314882150829468584ll;
    const LL U1 = 427197303358170108ll;
    const LL U2 = 1022292690726729920ll;
    const LL U3 = 1698479428772363217ll;
    const LL U4 = 2006101093849356424ll;
    const LL mod= 2009731336725594113ll;
    LL ArrU[10] = {U0,U1,U2,U3,U4};
    LL u[10];
    LL f(LL x)
    {
       return (x%mod)%2019;
    }
    LL a[1000000+100];
    LL fast_mul(LL a,LL b) //俄罗斯农名乘法 
    {
       if(a<b) swap(a,b);
       LL ans = 0;
       while(b)
       {
           if(b&1) ans = (ans+a)%mod;
           a = (a<<1)%mod;
           b/=2;
       }
       return ans;
    } 
    int main()
    {
    //    freopen("in2.txt","r",stdin);
       for(int i=0;i<5;++i)
           u[i] = ArrU[i]%mod;
       int n,q;
       scanf("%d%d",&n,&q);
       for(int i=1;i<=n;++i)
           a[i] = i;
       int L,R;
       while(q--)
       {
           scanf("%d%d",&L,&R);
           LL sum = 0;
           for(int i=L;i<=R;++i)
               sum += a[i]%2019;
           printf("%lld\n",sum);
           int j = sum%5;
    
           for(int i=L;i<=R;++i)
               a[i] = fast_mul(a[i],u[j]);
       }
    
       return 0;
    }

    附:我写的python暴力用于对拍

    # E.py
    # Author:Apare_xzc
    # 2019.12.16 11:03
    U0 = 314882150829468584
    U1 = 427197303358170108
    U2 = 1022292690726729920
    U3 = 1698479428772363217
    U4 = 2006101093849356424
    mod = 2009731336725594113
    u = [U0,U1,U2,U3,U4]
    def f(x):
       return (x%mod)%2019
    def main():
       fileName = input('请输入文件名:') # 带拓展名
       with open(fileName,'r') as IN:
           line = IN.readline() # 读入n,m
           n,m = map(int,line.split())
           a = [x for x in range(0,n+1)] # lambda表达式生成列表
           while m>0:
               m -= 1
               L,R = map(int,IN.readline().split())
               sum = 0
               for i in range(L,R+1): # 求和
                   sum += f(a[i])
               print(sum)
               j = sum % 5
               for i in range(L,R+1): #更新
                   a[i] *= u[j]      
    if __name__ == '__main__':
       main()
    #我真是个天才

写完啦~

xzc 2019.12.15 21:36


在这里插入图片描述
PS: 欢迎朋友们来加我QQ: 1363581749 快乐的xzc


D. 区块链

(题面是从博主[wingrez]的博客抄来的,感谢~)

题面:在这里插入图片描述

输入:在这里插入图片描述

输出在这里插入图片描述

Sample Input 1

5 10
1 2
1 3
1 4
1 5
2 3
2 4
2 5
3 4
3 5
4 5
1 27
1 1 1
2 1 2
3 1 3
4 1 4
5 1 5
1 1
2 1
3 1
4 1
5 1
1 2
2 2
3 2
4 2
5 2
1 10 10
2 11 9
1 11
2 11
3 11
4 11
5 11
1 12
2 12
3 12
4 12
5 12

Sample Output 1

2 0 1
2 0 2
2 0 3
2 0 4
2 0 5
2 0 1
2 0 1
2 0 1
2 0 1
2 0 1
3 0 1 10
4 0 1 10 9
3 0 1 10
3 0 1 10
3 0 1 10
4 0 1 10 9
4 0 1 10 9
4 0 1 10 9
4 0 1 10 9
4 0 1 10 9

Sample 1 解释在这里插入图片描述

Sample Input 2

15 13
1 2
2 3
3 4
4 5
1 6
6 7
7 8
8 9
1 10
10 11
11 12
12 13
14 15
6 28
1 1 1
1 2 2
1 6
2 7
13 7
9 7
5 7
3 14
8 14
5 14
11 14
9 25
5 25
13 25
9 29 3
5 29 4
13 29 5
1 53
2 59 6
2 59
1 1000
3 1000
8 1000
9 1000
10 1000
13 1000
14 1000
15 1000

Sample Output 2

3 0 1 2
2 0 1
1 0
1 0
1 0
3 0 1 2
1 0
1 0
3 0 1 2
2 0 1
2 0 1
2 0 1
4 0 1 2 3
5 0 1 2 3 6
5 0 1 2 3 6
5 0 1 2 3 6
5 0 1 2 3 6
5 0 1 2 3 6
5 0 1 2 3 6
5 0 1 2 3 6
1 0
1 0

数据范围

在这里插入图片描述

#题解##技术栈##C/C++#
全部评论
码力很强!
点赞 回复
分享
发布于 2019-12-16 17:18
写的是c++11 忘记设置c11的标准提交了,导致0分
点赞 回复
分享
发布于 2019-12-21 10:35
联想
校招火热招聘中
官网直投

相关推荐

4 7 评论
分享
牛客网
牛客企业服务