牛客小白月赛#59(A~F)

牛客小白月赛#59

A.我会开摆

  • 题意

    • 给定n=4的方阵,问是否存在n=2的方阵中四个格子全是一个字符的情况
  • 题解

    • 直接枚举所有点当n=2方阵的左上角,看是否符合题目要求
    • 注意:检查n=2的方阵时,要先排除某点为左上角没有n=2方阵情况
  • 代码

#include <iostream>

using namespace std;
char a[4][4];

bool check(int x,int y) {//检查以该点为左上角的,n=2的方阵是否符合要求
    char c=a[x][y];
    for(int i=x;i<x+2;i++)
        for(int j=y;j<y+2;j++)
            if(i<4 && j<4 && a[i][j]!=c) return 0;
            
    if(x<3 && y<3) return 1;
    return 0;
}

bool solve() {
    for(int i=0;i<4;i++) 
        for(int j=0;j<4;j++)
            cin>>a[i][j];
    
    for(int i=0;i<4;i++)//枚举所有点
        for(int j=0;j<4;j++)
            if(check(i,j)) return 1;
            
    return 0;
}

int main() {
    int t; cin>>t;
    while(t--) cout<<(solve() ? "Yes":"No")<<'\n';
    
    return 0;
}

B.走廊的灯

  • 题意

    • 路上有n盏灯,0代表灭的,1代表亮的,2代表闪烁,可以看成灭的也可以看成亮的
    • 问最长的状态一样的连续的灯的数量为多少
  • 题解

    • 双指针O(n);每次找到12连续段或者02连续段
    • 暴力O(n*n);数据比较水,不需要双指针。只要枚举以每一个点为起点往后找状态相同的灯,记录最大值即可
    • 注意(代码实现为找同一状态的灯要注意这条,如果遍历时代码实现只看不符合要求的那就不需要看这条):如果2开头,参照的灯的状态为,从左往右第一个不为2的灯的状态
  • 代码

双指针

#include <iostream>
#include <cstring>

using namespace std;

void solve() {
    int n; cin>>n;
    string s; cin>>s;
    
    int res=1;
    for(int i=0;i<n;i++) {//统计02连续段,因为02段是被1隔开,所以只要不是1就行
        if(s[i]!='1') {
            int j=i;
            while(j<n && s[j]!='1') j++;
            res=max(res,j-i);
            j--; i=j;
        }
    }
    for(int i=0;i<n;i++) {//统计12连续段,同理
        if(s[i]!='0') {
            int j=i;
            while(j<n && s[j]!='0') j++;
            res=max(res,j-i);
            j--; i=j;
        }
    }
    
    cout<<res<<'\n';
}

int main() {
    int t; cin>>t;
    while(t--) solve();
    
    return 0;
}

暴力

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

void solve() {
    int n; cin>>n;
    string s; cin>>s;
    
    int res=1;
    for(int i=0;i<n;i++) {
        int j=i;
        while(j<n && s[j]=='2') j++;//找到第一个不为2的灯,以它作为参照状态
        char c=s[j];
        while(j<n && (s[j]=='2' || s[j]==c)) j++;//找后面连续的灯
        res=max(res,j-i);//更新答案
    }
    cout<<res<<'\n';
}

int main() {
    int t; cin>>t;
    while(t--) solve();
    
    return 0;
}

C.输出练习

  • 题意

    • 从小到大输出区间[l,r]中,k的非负整数次方的值
    • l,r,k范围 [0,2^63)
  • 题解

    • 除去特例的0,1;当k=2时,l=0,r=2^63-1,会有最多个输出,也才64个。所以直接暴力即可
    • 注意特判。k=1时,直接输出1即可,否则暴力和会炸;k=0时,0^0=1,但是0^1=0,0^2=0,即如果区间符合要求,那么k=0会有两个输出。麻麻麻这里WA麻了,好无语
    • 注意数据范围,while(res<=r)这样写会爆long long,应该写成while(res<=r/k) ,又WA好几发,烦死了
  • 代码

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;
typedef long long ULL;

void solve() {
    ULL l,r,k;
    cin>>l>>r>>k;
    ULL res=1,cnt=0; 
    if(k==0 && l<=0 && r>=0) cout<<0<<' ',cnt++;//特判
    if(l<=1 && r>=1) cout<<1<<' ',cnt++;
    
    while(k&&k!=1 && res<=r/k) {//数据范围,这样写防止爆long long
        res*=k;
        if(res>=l && res<=r) cout<<res<<' ',cnt++;
    }
    if(cnt==0) cout<<"None.";
    cout<<'\n';
}

int main() {
    int t; cin>>t;
    while(t--) solve();
    
    return 0;
}

D.国际象棋

  • 题意

    • 竖直放置的n*m的棋盘,每次选一列放一枚棋子,重力原因棋子落到最下面
    • 对于第i个放置的棋子,i为奇数时,其所放的颜色为黑色,偶数为白色棋子
    • k子连珠定义为:横竖撇捺四个方向存在某个方向有k个相同颜色棋子
    • 一共放t颗棋子,若出现k子连珠则游戏结束,保证有解,问游戏结束时放置了多少棋子
  • 题解

    • 模拟题。

      需要维护的变量:
      棋盘的状态 -> g[n][m]=1/2,来表示棋盘及棋子的状态
      当前列放棋子落在第几行 -> h[i]表示第i列的高度(行数)
      
      需要维护的操作:
      检验当前有无k子连珠 -> check(x,y),以(x,y)为中心去检验四种方向,每种方向正负两个方向去记录连续的相同颜色的棋子数量,数量大于等于k即符合要求
      
  • 代码

#include <iostream>

using namespace std;
const int N=1005;

int n,m,k,t;
int g[N][N],h[N];
int dx[4]={1,0,1,-1},dy[4]={0,-1,-1,-1};//单位向量的定义为横竖撇捺四个方向


bool check(int x,int y) {//检验以(x,y)为中心是否存在k子连珠
    for(int d=0;d<=3;d++) {//横竖撇捺四种方向检查
        int cnt=0;//此方向连续的相同颜色棋子数量
        int dxx=dx[d],dyy=dy[d];//偏移量
        int tx=x,ty=y;
      //不越界且连续颜色相同,正方向有多少个
        while(1<=tx&&tx<=n&&1<=ty&&ty<=m && g[tx][ty]==g[x][y]) {
            cnt++; tx+=dxx; ty+=dyy;
        }
        
      //不越界且连续颜色相同,负方向有多少个
        tx=x,ty=y;
        while(1<=tx&&tx<=n&&1<=ty&&ty<=m && g[tx][ty]==g[x][y]) {
            cnt++; tx-=dxx; ty-=dyy;
        }
        cnt--;//多记录了(x,y)自身,去除
        if(cnt>=k) return 1;
    }
    return 0;
}

int main() {
    cin>>n>>m>>k>>t;
    for(int i=1;i<=t;i++) {//注意是t个棋子
        int x; cin>>x;
        h[x]++;//选择该列,该列的高度增加;注意题目的棋盘是从1开始的
        g[h[x]][x]=((i&1) ? 2:1);//black:2;注意颜色是由输入顺序定义的
        
        if(check(h[x],x)) {cout<<i<<'\n'; return 0;}//有答案输出
    }
    
    return 0;
}

E.弹珠碰撞

  • 题意

    • 长度为n的线段上有m个弹珠,给定每个弹珠的方向左/右,及坐标
    • 弹珠每秒走一个单位,发生碰撞会停滞一秒,且两珠方向互换,多次碰撞停滞时间叠加
    • 当弹珠走到0,或者n+1认为掉落
    • 问线段上最后掉落的弹珠掉出所花的时间
  • 题解

    • 碰撞转向直接看成穿过就好了,因为弹珠之间没有区别
    • 以某个向左走于位置i的弹珠为例,没有碰撞时时间为i-0,有碰撞需要记录在i左边且方向向右的弹珠数r,时间为i-0+r,从左往右边遍历遍记录答案和数量即可。某个向右走于位置i的弹珠,n+1-i+l,注意这里是逆序遍历。
  • 代码

#include <iostream>

using namespace std;
const int N=1e6+10;

int n,m,d[N],p[N],seg[N];

int main() {
    cin>>n>>m;
    for(int i=1;i<=m;i++) cin>>d[i];
    for(int i=1;i<=m;i++) cin>>p[i],seg[p[i]]=d[i]+1;
    
    int l=0,r=0,ans=0;
    for(int i=1;i<=n;i++) {
        if(seg[i]==0) continue;
        if(seg[i]==1) ans=max(ans,i-0+r);
        else r++;
    }
    for(int i=n;i>=1;i--) {
        if(seg[i]==0) continue;
        if(seg[i]==2) ans=max(ans,n+1-i+l);
        else l++;
    }
    
    cout<<ans<<'\n';
    
    return 0;
}

F.困难卷积

  • 题意

    • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BcmuODbM-1666978509437)(/Users/chenyanling/Library/Application Support/typora-user-images/image-20221029002156595.png)]
  • 题解

    • trick题。如果我们直接暴力枚举两个数组,复杂度为O(n^2)炸了
    • 观察数据范围特征,数组的和范围在1e7以内,那么考虑与种类数有关,因为贪心构造一个数种类多的数组为0 1 … x,等差数列求和得到x=sqrt(2e7),即数组中最多有sqrt(2e7)种数,此时遍历的复杂度已经降低了,所以开始考虑如何计算答案
    • 以同种类数分别分组a,b后,我们可以得到数num及其出现的次数cnt。那么对于某一组别a1和某一组别b1对于答案的贡献为(a1.cnt * b1.cnt * floor(sqrt(差)),那么只需要枚举a分组和b分组,时间复杂度为O(sqrt(2e7)^2)=O(2e7)
  • 代码

#include <iostream>
#include <map>
#include <cmath>

using namespace std;
map<int,int> ha,hb;

int sqt(int x,int y) {
    return floor(sqrt(abs(x-y)) + 1e-6);//下取整如果是整数计算,加一点精度即可,否则答案不正确
}

int main() {
    int n; cin>>n;
    for(int i=0;i<n;i++) {
        int x; cin>>x;
        ha[x]++;
    }
    for(int i=0;i<n;i++) {
        int x; cin>>x;
        hb[x]++;
    }
    
    long long res=0;
    for(auto u:ha) 
        for(auto v:hb)
            res+=1ll*u.second*v.second*sqt(u.first,v.first);
    cout<<res<<'\n';
    
    return 0;
}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值