zoukankan      html  css  js  c++  java
  • AtCoder Beginner Contest 148 题解

    AtCoder Beginner Contest 148 题解

    前言

    包含题目:

    ABC148 A ABC148 B ABC148 C ABC148 D ABC148 E ABC148 F

    这次比赛坑好多啊(虽然水了),也可能是我的实现方法不对qaq。

    A - Round One

    题意

    你有三个数:(1, 2, 3),现在其中两个数不对,输出对的那个数。

    做法

    建立一个集合,删去坏的,输出剩下的一个。

    用三个if语句判断即可。

    程序

    考场做法:

    #include<bits/stdc++.h>
    using namespace std;
    
    set<int> t={1,2,3};
    int a,b;
    
    int main(){
    
        ios::sync_with_stdio(false);
        cin.tie(0);
        cout.tie(0);
    
        cin>>a>>b;
        t.erase(a);
        t.erase(b);
        cout<<*t.begin()<<endl;
    
        return 0;
    }
    

    真实做法:

    #include<bits/stdc++.h>
    using namespace std;
    
    int a,b;
    
    int main(){
    
        ios::sync_with_stdio(false);
        cin.tie(0);
        cout.tie(0);
        //三行头文件,可以优化cin、cout速度(但仅能聊以慰籍)
    
        cin>>a>>b;
        if(a>b)swap(a,b);//swap用作交换两个同一类型的变量的值,此处确保a<b
        if(a==1&&b==2)cout<<3;
        if(a==1&&b==3)cout<<2;
        if(a==2&&b==3)cout<<1;
    
        return 0;
    }
    

    B - Strings with the Same Length

    题意

    给你长度(N)的字符串(S)(T),让你把它们合并起来,方法如下:

    (S)的开头字符,删去,添加到答案串的末尾。拿(T)的开头字符,删去,添加到答案串的末尾。

    重复以上操作(N)次,此时(S)(T)应该是空串。

    做法

    暴力模拟即可,不过不用真的删去字符,从前往后跑就可以了。

    程序

    #include<bits/stdc++.h>
    using namespace std;
    
    int n;
    string s,t,ans;
    
    int main(){
    
        ios::sync_with_stdio(false);
        cin.tie(0);
        cout.tie(0);
    
        cin>>n>>s>>t;
        for(int i=0;i<n;i++){//从前往后跑s和t的字符
            ans+=s[i];
            ans+=t[i];//向ans的末尾添加字符
        }
        cout<<ans<<endl;
    
        return 0;
    }
    

    C - Snack

    题意

    给你(A)(B),让你求一个数,使得它能被(A)(B)整除吗,同时最小(也就是最小公倍数)。

    做法

    使用辗转相除法先求出最大公因数,对于(A)(B),它们的递归的最大公因数求法(f(A,B))如下:

    (保证它们都是正整数

    • (B)(0),此时返回(A)
    • 返回(f(B,Amod B))

    (A imes B div f(A,B))就是最小公倍数。

    程序

    #include<bits/stdc++.h>
    using namespace std;
    
    int gcd(int a,int b){//用前文的方法求最大公因数
        if(b==0)return a;
        else return gcd(b,a%b);
    }
    
    int main(){
    
        ios::sync_with_stdio(false);
        cin.tie(0);
        cout.tie(0);
    
        int a,b;
        cin>>a>>b;
        cout<<((long long)(a/gcd(a,b)))*b<<endl;//要用long long,不然会爆int
    
        return 0;
    }
    

    D - Brick Break

    题意

    给你(N)个砖块,每个砖块写有数字(a_i),问你删去一些砖块(最多删去(N-1)块)后,剩下的(K)块砖块,是一个形如(1,2,3,dots,K)的序列时,(K)最大是多少,如果不能有这样的序列,那么就输出-1

    做法

    其实就是求一个形如(1,2,3,dots,K)(a_i)的子序列(子序列的定义是元素可以不连续的),可以(O(N))完成。

    不考虑-1,只求出(K)最大是多少,那么如果(K=0),就输出-1,否则输出(K)

    (K)的求法:

    记录当前的序列的最后一个的值(c)(也就是序列的长度)(初始为(0)),从前往后跑(a_i)中的元素,如果是(c+1),那么就把(c)加一。这样最后它就是(K)了。

    程序

    #include<bits/stdc++.h>
    using namespace std;
    
    int n;
    int a[200005];
    
    int main(){
    
        ios::sync_with_stdio(false);
        cin.tie(0);
        cout.tie(0);
    
        cin>>n;
        int c=1;//此处c的值是当前序列的最后一个的值加一,当时写的时候太急,失误了
        for(int i=0;i<n;i++){
            cin>>a[i];
            if(a[i]==c){//如同做法中描述的,是序列长度加一时,就增加序列长度
                c++;
            }
        }
        cout<<(n-c+1==n?-1:n-c+1)<<endl;//序列长度为c-1,那么n-c+1就表示删去的东西的个数
    
        return 0;
    }
    

    E - Double Factorial

    题意

    给你一个数(n)

    定义一个函数(f(n))

    • (f(n)=1) (if (n<2))
    • (f(n)=nf(n−2)) (if (n≥2))

    输出函数值的末尾的0的个数。

    做法

    很容易就可以看出,后缀零的个数只和函数值(2)(5)因子的个数有关。

    由于(f(n))只要求了(f(n-2)),所以奇偶性肯定不会变的,(n)为奇数时不可能包含(2)作为因数,特判一下。

    我们再来看偶数,那么我们可以这么计算(2)的因子的个数:

    (i)(1)开始递增,查看包含(2^i)作为因子的数的个数(也就是(lfloor { frac{n}{2^i} } floor),此处(lfloor a floor)指不超过(a)的最大整数,也就是下取整),把函数值中的(2)的因子个数加上这个个数,你可能会问了,它包含的因数不应该是(lfloor { frac{n}{2^i} } floor imes i)吗?其实,里面的(lfloor { frac{n}{2^i} } floor imes ( i - 1 )),已经在(lfloor { frac{n}{2^{i-1}} } floor)中加过了,所以就是(lfloor { frac{n}{2^i} } floor)就可以了。

    对于(5)的因数,我们可以大致使用同样的方法求,但是每次对于答案的贡献是(lfloor { frac{n}{2 imes 5^i} } floor),因为必须是偶数,所以需要再乘一个(2)以确保只计算了偶数。具体原因如下:

    我们举个例子,列出(1,2,3,dots,25)(5)的因子的个数:

    [0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,2 ]

    总和是(6)

    那么,只计算偶数的话,就只有这样的序列:

    [N,0,N,0,N,0,N,0,N,1,N,0,N,0,N,0,N,0,N,1,N,0,N,0,N ]

    (N)代表奇数,舍去了)总和是(2)

    现在可以理解为什么要乘以(2)了吧。

    程序

    #include<bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    
    ll n,c2,c5;
    
    int main(){
    
        ios::sync_with_stdio(false);
        cin.tie(0);
        cout.tie(0);
    
        cin>>n;
        if(n&1){
            cout<<0<<endl;
            return 0;
        }
        for(ll i=2;i<=n;i*=2){
            c2+=n/i;
        }
        for(ll i=10;i<=n;i*=5){
            c5+=n/i;
        }
        cout<<min(c2,c5)<<endl;
    
        return 0;
    }
    

    F - Playing Tag on Tree

    题意

    有一棵(N)个结点的树,高桥和青木在上面的(u)(v)结点上玩抓人游戏。每个回合如下:

    1. 如果高桥和青木在同一个结点上,游戏结束。高桥选一个和当前结点相邻的结点,走过去。
    2. 如果高桥和青木在同一个结点上,游戏结束。青木选一个和当前结点相邻的结点,走过去。
    3. 开始下一个回合

    轮到某人走时,不能停留在同一个结点。

    高桥希望回合数尽可能多,青木希望回合数尽可能少,两人都采用最优策略

    输出游戏结束时青木走的步数

    做法

    首先,青木的最优策略很好确定,就是走通向的当前高桥的位置的简单路径就可以了。同时,不论高桥的位置怎样变化,青木走的路径都会沿着通向最终会抓到高桥的结点的这条道路,证明很难,我就不证明了。感性地理解,就是由于高桥和青木不会在途中相遇,所以青木走向的那个(以当前青木所在结点为根的)子树肯定包含高桥和游戏结束时的高桥的终点。

    高桥的呢,有点麻烦,所以我们就搜索一下吧qwq(暴力、大好き)。

    首先,我们预处理出青木到每个结点的距离,之后搜索高桥的路径(深搜、广搜皆可)。对于当前结点是否可以走,只需要满足高桥到这里的距离严格小于青木到这里的距离即可。特殊地,由于只能移动,不能停留在同一个结点,所以到了叶子结点时,高桥会在相邻结点和这个叶子结点之间重复移动,最终抓到的位置一定是在相邻结点上(一共两种情况,自己手推一下就好了)。

    程序

    #include<bits/stdc++.h>
    using namespace std;
    
    int n,ta,ao,ans;
    vector<int> g[100005];
    int dis[100005];
    
    void dfs(int x,int p,int d){
        if(d>=dis[x]){//如果被抓到了就退出
            ans=max(ans,dis[x]);
            return;
        }
        for(int i=0;i<g[x].size();i++){
            int &y=g[x][i];
            if(y!=p){
                dfs(y,x,d+1);
            }
        }
        if(g[x].size()==1){//是叶子结点,就特判一下之前做法中提到的情况
            ans=max(ans,dis[x]-1);
        }
    }
    
    int main(){
    
        ios::sync_with_stdio(false);
        cin.tie(0);
        cout.tie(0);
    
        cin>>n>>ta>>ao;
        for(int i=1;i<n;i++){
            int a,b;
            cin>>a>>b;
            g[a].push_back(b);
            g[b].push_back(a);
        }
        memset(dis,-1,sizeof(dis));
        dis[ao]=0;
        {//预处理青木到每个结点的距离dis
            queue<int> q;
            q.push(ao);
            while(!q.empty()){
                int x=q.front();q.pop();
                for(int i=0;i<g[x].size();i++){
                    int &y=g[x][i];
                    if(dis[y]==-1){
                        dis[y]=dis[x]+1;
                        q.push(y);
                    }
                }
            }
        }
        dfs(ta,-1,0);
        cout<<ans<<endl;
    
        return 0;
    }
    

    结束语

    感谢观看,感觉自己写得很high,完全没有考虑到别人的观感qaq。所以,有什么意见就评论吧<3<3<3,我一定会改的qaq。

  • 相关阅读:
    RabbitMQ学习之集群消息可靠性测试
    RabbitMQ学习之集群镜像模式配置
    (转)RabbitMQ学习之集群部署
    RabbitMQ学习之集群模式
    RabbitMQ学习之spring配置文件rabbit标签的使用
    RabbitMQ学习之messageconver插件实现(Gson)
    RabbitMQ学习之基于spring-rabbitmq的RPC远程调用
    RabbitMQ学习之基于spring-rabbitmq的消息异步发送
    (转)RabbitMQ学习之spring整合发送异步消息(注解实现)
    范仁义js课程---16、非bool值的逻辑运算
  • 原文地址:https://www.cnblogs.com/BlahDuckling747/p/12081441.html
Copyright © 2011-2022 走看看