2020-2-15模拟赛题解

前言

这场比赛我发挥欠佳,各种小失误不断,也需要我自己去反思吧,以后打模拟赛也要有选择性地去打,毕竟一天 \(3\) 场比赛肯定会有影响。

正文

1. 子数整数

错点

考试的时候蒟蒻把 No 打成了 NO

分析

这道题实际上就是个小模拟,您只要会 \(for\) 循环就可以轻松过掉此题。

答案是直接统计一下就好了。

我们可以直接从 1000030000 循环,算出 s1s2s3

for(int i=10000;i<=30000;i++){
    int g=i%10,s=i/10%10,b=i/100%10,q=i/1000%10,w=i/10000%10,s1=100*w+10*q+b,s2=100*q+10*b+s,s3=100*b+s*10+g;//s1,s2,s3的计算
    if(s1%k==0&&s2%k==0&&s3%k==0)writen(i),f=1;
}

总代码

#include <bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T &FF){
    T RR=1;FF=0;char CH=getchar();
    for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
    for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
    FF*=RR;
}
template<typename T>void write(T x){
    if(x<0)putchar('-'),x*=-1;
    if(x>9)write(x/10);
    putchar(x%10+48);
}
template<typename T>void writen(T x){
    write(x);
    puts("");
}
int k,s1,s2,s3,g,s,b,q,w;
bool f;
int main(){
    read(k);
    for(int i=10000;i<=30000;i++){
        int g=i%10,s=i/10%10,b=i/100%10,q=i/1000%10,w=i/10000%10,s1=100*w+10*q+b,s2=100*q+10*b+s,s3=100*b+s*10+g;//s1,s2,s3的计算
        if(s1%k==0&&s2%k==0&&s3%k==0)writen(i),f=1;
    }if(f==0)puts("No");//考试时这里直接挂掉,打成了NO
    return 0;
}

2. 安全逃离

错点

蒟蒻向老师报告题目有问题后没有认真看题,结果就死了。

分析

做到这道题的时候,我们考到数据范围很小,考虑高复杂度的做法。

我的做法是最暴力的做法,复杂度大概是 \(O(cr^2 \times + rc^2 \times n)\),面对这么小的这么小的数据,这个做法显然能过。

我们先写一个 \(check\) 函数,判断此时的矩阵是否安全。

bool check(){
    for(int i=1;i<=r;++)
        for(int j=1;j<=c;j++)
            if(a[i][j]){//此点有牛
                int s=0;
                for(int l=1;l<=i;l++)
                    if(a[l][j]){
                        s++;
                        break;
                    }
                for(int l=j;l<=n;l++)
                    if(a[i][l]){
                        s++;
                        break;
                    }
                if(s==2)return false;//如果有牛上面和右面都被堵死了,这个矩阵显然是不安全的
            }
    return true;
}

当然还有更快速地写法啦

bool save(){
    for(int i=1;i<=k;i++){
        int flag=0;
        for(int l=x[i]-1;l>=1;l--)
            if(a[l][y[i]]){
                flag++;
                break;
            }
        for(int l=y[i]+1;l<=n;l++)
            if(a[x[i]][l]){
                flag++;
                break;
            }
        if(flag==2)return false;
    }
    return true;
}

我们再写一个函数去枚举移掉哪头牛

void work(){
    int f=1;
    for(int i=1;i<=k;i++){
        a[x[i]][y[i]]--;
        if(save()){//如果安全
            f=0;
            write(i);//输出
            puts("");
        }
        a[x[i]][y[i]]++;
    }if(f)puts("-1");//无解
}

总代码

#include <bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T &FF){
    T RR=1;FF=0;char CH=getchar();
    for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
    for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
    FF*=RR;
}
template<typename T>void write(T x){
    if(x<0)putchar('-'),x*=-1;
    if(x>9)write(x/10);
    putchar(x%10+48);
}
const int MAXN=50+10,MAXK=100+10;
int a[MAXN][MAXN],n,m,k,x[MAXK],y[MAXK];
bool save(){
    for(int i=1;i<=k;i++){
        int flag=0;
        for(int l=x[i]-1;l>=1;l--)
            if(a[l][y[i]]){
                flag++;
                break;
            }
        for(int l=y[i]+1;l<=n;l++)
            if(a[x[i]][l]){
                flag++;
                break;
            }
        if(flag==2)return false;
    }
    return true;
}
void work(){
    int f=1;
    for(int i=1;i<=k;i++){
        a[x[i]][y[i]]--;//移除
        if(save()){
            f=0;
            write(i);
            puts("");
        }
        a[x[i]][y[i]]++;//注意回溯
    }if(f)puts("-1");
}
int main(){
    read(n);read(m);read(k);
    for(int i=1;i<=k;i++){
        read(x[i]);read(y[i]);
        a[x[i]][y[i]]++;
    }
    if(save())puts("0");
    else work();
    return 0;
}

3. 阶乘问题

错点

蒟蒻竟然把 \(/ 10\) 打成了 \(\% 10\)

分析

做法显然

由于数字极大,我们应该先对 \(1\)~\(n\) 的这些数字把末尾的 \(0\) 全部删掉

while(x%10==0)x/=10;

再把乘好后的 \(s\) 末尾的 \(0\) 也都去掉。

关于为什么末尾会有 \(0\),我来举个例子 \(2 \times 5=10\) ,所以我们还要再筛一遍 \(0\)

while(s%10==0)s/=10;

总代码

#include <bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T &FF){
    T RR=1;FF=0;char CH=getchar();
    for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
    for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
    FF*=RR;
}
template<typename T>void write(T x){
    if(x<0)putchar('-'),x*=-1;
    if(x>9)write(x/10);
    putchar(x%10+48);
}
int main(){
    int n,s=1;
    read(n);
    for(int i=1;i<=n;i++){
        int x=i;
        while(x%10==0)x/=10;
        s*=x;
        while(s%10==0)s/=10;
    }write(s);
    return 0;
}

4. 拱猪计分

分析

典型的大模拟。

明白了什么叫 \(1h\) 读题,\(0.2h\) 写代码

我们从读入开始

读入

for(int i=1;i<=4;i++){
    read(size[i]);
    for(int j=1;j<=size[i];j++){
        char k;int p;
        cin>>k>>p;
        if(k=='H')card[i][p]=true;
        else if(k=='S')card[i][14]=true;
               else if(k=='D')card[i][15]=true;
                else if(k=='C')card[i][16]=true;
    }
}

我们用 \(card_{i,j}\) 记录第 \(i\) 个人,有没第 \(j\) 张得分牌。

计分

for(int i=1;i<=4;i++){
    bool flag=true;
    for(int j=1;j<=13;j++)
        if(card[i][j]==false){
            flag=false;
            break;
        }
    if(flag){//有没有红心牌
        int ans=200;//所有红心牌以+200分计算。
        if(card[i][14]&&card[i][15])ans=500;//若S12、D11皆在吃下所有红心牌之一家,则此玩家得+500分。
        else ans=ans+card[i][14]*fraction[14]+card[i][15]*fraction[15];
        if(card[i][16])ans*=2;//若除了C10还有其它计分牌,则将其它计分牌所得分数加倍计算。
        work(ans);//输出
        putchar(32);//输出
    }else{
        bool flg=true;
        for(int j=1;j<=15;j++)
            if(card[i][j]==true){
                flg=false;
                break;
            }
        if(flg){
            if(card[i][16])work(50);//若持有C10的玩家只有该张牌而没有任何其它牌则得+50分
            else work(0);//若未持有这16张牌之任一张则以得零分计算。
            putchar(32);//输出
        }else{
            int ans=0;
            for(int j=1;j<=15;j++)
                if(card[i][j])ans+=fraction[j];
            if(card[i][16])ans*=2;
            work(ans);//输出
            putchar(32);//输出
        }
    }
}puts("");

总代码

#include <bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T &FF){
    T RR=1;FF=0;char CH=getchar();
    for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
    for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
    FF*=RR;
}
template<typename T>void write(T x){
    if(x<0)putchar('-'),x*=-1;
    if(x>9)write(x/10);
    putchar(x%10+48);
}
void work(int x){
    if(x<0)cout<<x;
    else if(x>0)cout<<'+'<<x;
        else cout<<x;
}
const int fraction[17]={0,-50,-2,-3,-4,-5,-6,-7,-8,-9,-10,-20,-30,-40,-100,+100,0};
int size[5];
bool card[5][17];
int main(){
    while(1){
        memset(card,false,sizeof(card));
        for(int i=1;i<=4;i++){
            read(size[i]);
            for(int j=1;j<=size[i];j++){
                char k;int p;
                cin>>k>>p;
                if(k=='H')card[i][p]=true;
                else if(k=='S')card[i][14]=true;
                    else if(k=='D')card[i][15]=true;
                        else if(k=='C')card[i][16]=true;
            }
        }
        if(size[1]+size[2]+size[3]+size[4]==0)return 0;
        for(int i=1;i<=4;i++){
            bool flag=true;
            for(int j=1;j<=13;j++)
                if(card[i][j]==false){
                    flag=false;
                    break;
                }
            if(flag){
                int ans=200;
                if(card[i][14]&&card[i][15])ans=500;
                else ans=ans+card[i][14]*fraction[14]+card[i][15]*fraction[15];
                if(card[i][16])ans*=2;
                work(ans);
                putchar(32);
            }else{
                bool flg=true;
                for(int j=1;j<=15;j++)
                    if(card[i][j]==true){
                        flg=false;
                        break;
                    }
                if(flg){
                    if(card[i][16])work(50);
                    else work(0);
                    putchar(32);
                }else{
                    int ans=0;
                    for(int j=1;j<=15;j++)
                        if(card[i][j])ans+=fraction[j];
                    if(card[i][16])ans*=2;
                    work(ans);
                    putchar(32);
                }
            }
        }puts("");
    }
    return 0;
}

5. 变色龙

错点

\(bfs\)\(priority\)_\(queue\) 优化写假了。

分析

最短路问题。

为什么能使用 \(dij\) 跑最短路?明显会 \(T\) 啊!

点数:\(2000 \times 2000=400,0000\)

边数:小学奥数之等差数列

\(\begin{matrix}\underbrace{0+4+\cdots+7996}\\2000项\end{matrix}=(7996+0) \times 2000 \div 2=7996 \times 1000=799,6000\)

我们知道 \(dij\) 的复杂度是 \(O(n \log m)\)

简单计算一下

\(\log m=\log 7996000 \approx 26\)\(26 \times 4000000=1,0400,0000 > 1,0000,0000‬\)

所以显然是会 \(T\) 掉的(除非玄学卡常,比如:等式展开之类的)

\(bfs\) 的期望复杂度是 \(O(n+m)\) 的。

\(bfs\) 大概就是这种写法

q.push((node){xx,yy,0});
    while(q.size()){
    node f=q.top();
    q.pop();
    for(int i=0;i<4;i++){
        int x=f.x+dx[i],y=f.y+dy[i];
        if(x>=1&&x<=n&&y>=1&&y<=m){
            int as=ans[f.x][f.y];
            if(a[x][y]!=a[f.x][f.y])as++;
            if(as<ans[x][y])ans[x][y]=as,q.push((node){x,y});
        }
    }
}

总代码

#include <bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T &FF){
    T RR=1;FF=0;char CH=getchar();
    for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
    for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
    FF*=RR;
}
template<typename T>void write(T x){
    if(x<0)putchar('-'),x*=-1;
    if(x>9)write(x/10);
    putchar(x%10+48);
}
struct node{
    int x,y;
};
const int MAXN=2000+10;
int n,m,xx,yy,a[MAXN][MAXN],ans[MAXN][MAXN];
int dx[4]={-1,1,0,0};
int dy[4]={0,0,-1,1};
queue<node>q;
int main(){
    read(n);read(m);read(xx);read(yy);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            read(a[i][j]);
    q.push((node){xx,yy,0});
    while(q.size()){
        node f=q.top();
        q.pop();
        for(int i=0;i<4;i++){
            int x=f.x+dx[i],y=f.y+dy[i];
            if(x>=1&&x<=n&&y>=1&&y<=m){
                int as=ans[f.x][f.y];
                if(a[x][y]!=a[f.x][f.y])as++;
                if(as<ans[x][y])ans[x][y]=as,q.push((node){x,y});
            }
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++)
            cout<<ans[i][j]<<" ";
        cout<<endl;
    }
    return 0;
}

6. 和谐分组

讲解二分

终于是正常一点的题目了,并且是我最喜欢的算法(蒟蒻最喜欢的算法是并查集、二分、dp、LCA)

二分是确定一个答案然后对其分析,而答案常常有这样一种情况:

左闭右开

​ 左闭右开

右闭左开

​ 右闭左开

题目通常会让我们找符合条件的最大值或最小值。

以这道题为例,就是去在可能的谐度度中找一个最小的。

比答案大的都可以,不答案小的都不可以。

这个我们叫右闭左开。

而比答案小的都可以,不答案大的都不可以。

这个我们叫左闭右开。

二分顾名思义,就是二分。

左闭右开:

int l=0,r=INT_MAX/2;
while(l+1<r){
    int mid=(l+r)>>1;
    if(check(mid))l=mid;//这里不同
    else r=mid;//这里不同
}

右闭左开:

int l=0,r=INT_MAX/2;
while(l+1<r){
    int mid=(l+r)>>1;
    if(check(mid))r=mid;//这里不同
    else l=mid;//这里不同
}

分析

现在我们已经会了二分,我们可以愉快地做这道题了。

我们可以开始写 \(check\) 函数:

bool check(int x){//check函数顾名思义,就是用来反对我们的答案x是否可行
    int maxn=a[1],minn=a[1],s=1;//初始化
    for(int i=2;i<=n;i++){
        maxn=max(maxn,a[i]);//最大
        minn=min(minn,a[i]);//最小
        if(maxn-minn>x){
            s++;//新分一个组
            maxn=a[i];//初始化
            minn=a[i];//初始化
        }
    }
    return s<=k;//如果组数小于等于k,就说明不可行
}

总代码

#include <bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T &FF){
    T RR=1;FF=0;char CH=getchar();
    for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
    for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
    FF*=RR;
}
template<typename T>void write(T x){
    if(x<0)putchar('-'),x*=-1;
    if(x>9)write(x/10);
    putchar(x%10+48);
}
const int MAXN=1e5+10;
int n,k,a[MAXN];
bool check(int x){
    int maxn=a[1],minn=a[1],s=1;
    for(int i=2;i<=n;i++){
        maxn=max(maxn,a[i]);
        minn=min(minn,a[i]);
        if(maxn-minn>x){
            s++;
            maxn=a[i];
            minn=a[i];
        }
    }
    return s<=k;
}
int main(){
    read(n);read(k);
    for(int i=1;i<=n;i++)read(a[i]);
    int l=0,r=INT_MAX/2;
    while(l+1<r){
        int mid=(l+r)>>1;
        if(check(mid))r=mid;
        else l=mid;
    }write(r);
    return 0;
}
posted @ 2020-03-06 17:11  zhaohaikun  阅读(185)  评论(0编辑  收藏  举报