牛客小白月赛51题解

选择题

原题链接

思路

n n n为奇数 → \rightarrow 选择一个奇数和一个偶数 → \rightarrow 最大奇数n-2
n n n为偶数 → \rightarrow 选择一个奇数和一个偶数 → \rightarrow 最大奇数n-1

算法标签

思维 数学

代码

#include<bits/stdc++.h>
#define int long long
#define rep(i, a, b) for(int i=a;i<b;++i)
#define Rep(i, a, b) for(int i=a;i>b;--i)
using namespace std;
const int N = 10005;
inline int read(){
   int s=0,w=1;
   char ch=getchar();
   while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
   while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
   return s*w;
}
void put(int x) {
    if(x<0) putchar('-'),x=-x;
    if(x>=10) put(x/10);
    putchar(x%10^48);
}
signed main(){
    int n=read();
    if(n%2){
        put(n-2);
    }
    else{
       put(n-1); 
    }
}

填空题

原题链接

思路

遍历数组元素 → \rightarrow 若数组元素为零 → \rightarrow 按最小方法构造该元素, 即其值为前一个元素+1(该元素具有前一个元素) → \rightarrow 构造结束判断是否符合要求

算法标签

思维 构造

代码

#include<bits/stdc++.h>
#define int long long
#define rep(i, a, b) for(int i=a;i<b;++i)
#define Rep(i, a, b) for(int i=a;i>b;--i)
using namespace std;
const int N = 100005;
int a[N];
inline int read(){
   int s=0,w=1;
   char ch=getchar();
   while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
   while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
   return s*w;
}
void put(int x) {
    if(x<0) putchar('-'),x=-x;
    if(x>=10) put(x/10);
    putchar(x%10^48);
}
signed main(){
    int n=read();
    int sum=0;
    bool flag=false;
    rep(i, 0, n){
        a[i]=read();
        if(a[i])sum+=a[i];
        if(!(a[i])){
            if(i-1>=0)
            a[i]=a[i-1]+1, sum+=a[i];
            else
            a[i]=1, sum+=a[i];
        }
    }
    rep(i, 1, n){
        if(a[i]<=a[i-1]){
            flag=true;
        }
    }
    if(flag){
        put(-1);
    }
    else{
        put(sum);
    }
}

零一题

原题链接

思路

判断可构造条件 → \rightarrow 具有剩余0与剩余1 → \rightarrow 剩余0与剩余1个数为偶数 → \rightarrow 依次输出根据剩余0个数,剩余1个数,01串个数

算法标签

思维 构造

代码

#include<bits/stdc++.h>
#define int long long
#define rep(i, a, b) for(int i=a;i<b;++i)
#define Rep(i, a, b) for(int i=a;i>b;--i)
using namespace std;
const int N = 100005;
int a[N];
inline int read(){
   int s=0,w=1;
   char ch=getchar();
   while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
   while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
   return s*w;
}
void put(int x) {
    if(x<0) putchar('-'),x=-x;
    if(x>=10) put(x/10);
    putchar(x%10^48);
}
signed main(){
    int a=read(), b=read(), x=read();
    bool flag=false;
    if(!(x%2)){
        int aa=a-x/2;
        int bb=b-x/2;
        if(!(aa%2)&&!(bb%2)&&aa>=0&&bb>=0){
        if(aa){
            rep(i, 0, aa){
                put(0);
            }
        }
        if(bb){
            rep(i, 0, bb){
                put(1);
            }
        }
        rep(i, 0, x/2){
            put(0);
            put(1);
        }
        }
        else{
            put(-1);
        }
    }
}

操作题

原题链接

思路

n n n x x x取余之后所得值即需要加上的值,假若当 b b b乘以 x x x,就无法得到所加值。
随后将 b b b的值乘以 x x x后,补充 n n n x x x 取余的值,同理一直补充到n。

算法标签

思维

代码

#include<bits/stdc++.h>
#define int long long
#define x first
#define y second
#define rep(i, a, b) for(int i=a;i<b;++i)
#define Rep(i, a, b) for(int i=a;i>b;--i)
using namespace std;
const int N = 100005;
int a[N];
inline int read(){
   int s=0,w=1;
   char ch=getchar();
   while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
   while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
   return s*w;
}
void put(int x) {
    if(x<0) putchar('-'),x=-x;
    if(x>=10) put(x/10);
    putchar(x%10^48);
}
signed main(){
    int t=read();
    while(t--){
        int n=read(), xx=read();
        vector<pair<int, int>> ans;
        while(n){
            int yy=n%xx;
            rep(i, 0, yy){
                ans.emplace_back(1, 'a');
            }
            n/=xx;
            if(n){
                ans.emplace_back(2, 'b');
            }
        }
        put(ans.size());
        puts("");
        for(auto i:ans){
            printf("%lld %c\n", i.x, i.y);
        }
    }
    return 0;
}

语法题

原题链接

思路

只有当a单调上升才会有意义,若 a i a_i ai
比之前小,那么本次 a i a_i ai 就不需要计算。
其次我们需要能够快速求出 x / b i x/ b_i x/bi等于 n n n x x x的取值范围,其取值范围为 [ b i ∗ n , b i ∗ ( n + 1 ) − 1 ] [b_i*n,b_i*(n+1)-1] [bin,bi(n+1)1]。该取值范围与条件语句区间相交处即为所有符合情况的解,计算之后即可得到答案。
对于我们的条件语句,显然他的有效范围为 [ m a x a 1 . . . a i − 1 ] [max {a_1...a_{i-1}}] [maxa1...ai1]。最后特判,如果原 n n n并没有进入一个条件语句,那么他也是合法的。此时只有一个答案。
另外,由于long long int 类型的值的大小不能超过1e18数量级,但是这里乘法可能会导致超过,但是其实可以发现,如果一个数能够进入条件语句,那么他一定小于1e9,1e9除以一个正整数是一定不会得到一个1e9以上的结果的,所以如果n大于1e9,我们可以直接return。

算法标签

数学 思维

代码

#include<bits/stdc++.h>
#define int long long
#define x first
#define y second
#define rep(i, a, b) for(int i=a;i<b;++i)
#define Rep(i, a, b) for(int i=a;i>b;--i)
using namespace std;
const int N = 100005;
int a[N], b[N];
inline int read(){
   int s=0,w=1;
   char ch=getchar();
   while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
   while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
   return s*w;
}
void put(int x) {
    if(x<0) putchar('-'),x=-x;
    if(x>=10) put(x/10);
    putchar(x%10^48);
}
signed main(){
    int n=read();
    rep(i, 1, n+1){
        a[i]=read(),b[i]=read();
        assert(a[i]>=1&&a[i]<=1e9), assert(b[i]>=1&&b[i]<=1e9);    
    }
    int x=read(), ans=0, last=1;
    assert(x>=0&&x<=1e18);
    rep(i, 1, n+1){
        last=max(last, a[i]);
    }
    if(x>=last){
        ans++;
    }
    last=1;
    if(x>1e9){
        printf("%lld", ans);
    }
    rep(i, 1, n+1){
        int l=x*b[i], r=x*b[i]+b[i]-1;
        l=max(l, last);
        r=min(r, a[i]-1);
        last=max(a[i], last);
        if(l>r){
            continue;
        }
        ans+=(r-l+1);
    }
    printf("%lld\n", ans);
    return 0;
}

平均题

原题链接

思路

首先将区间长度相同进行合并。
例如:当长度为1的时候,所有长度为1的区间的和之和等于整个区间的和。
当长度为2的时间,所有长度为2的区间的和之和等于 ∑ i = 1 n − 1 a i + a i + 1 \sum_{i=1}^{n-1}{a_i+a_{i+1}} i=1n1ai+ai+1
我们可以发现,这个式子还可以等于
∑ i = 1 n − 1 a i + ∑ i = 2 n a i = ∑ i = 1 n a i + ∑ i = 2 n − 1 a i \sum_{i=1}^{n-1}{a_i}+\sum_{i=2}^{n}{a_i}=\sum_{i=1}^{n}{a_i}+\sum_{i=2}^{n-1}{a_i} i=1n1ai+i=2nai=i=1nai+i=2n1ai
当长度增加之后,我们可以发现长度为 i i i的区间的和等于长度为 i − 1 i-1 i1的区间的和加上 ∑ j = i j = n − i + 1 a i \sum_{j=i}^{j=n-i+1}{a_i} j=ij=ni+1ai
i > n / 2 i>n/2 i>n/2之后,他的值又等于 s n − i + 1 s_{n-i+1} sni+1
​ ,所以我们把之前计算的储存一下即可。

算法标签

数学 思维

代码

#include<bits/stdc++.h>
#define int long long
#define x first
#define y second
#define rep(i, a, b) for(int i=a;i<b;++i)
#define Rep(i, a, b) for(int i=a;i>b;--i)
using namespace std;
const int N = 100005;
const int mod = 1000000007;
int a[N];
int pre[N];
int sum=0, ans;
inline int read(){
   int s=0,w=1;
   char ch=getchar();
   while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
   while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
   return s*w;
}
void put(int x) {
    if(x<0) putchar('-'),x=-x;
    if(x>=10) put(x/10);
    putchar(x%10^48);
}
inline int pw(int a,int b){
    int res=1;
    while(b){
        if(b&1)
            res=res*a%mod;
        b>>=1;
        a=a*a%mod;
    }
    return res;
}
signed main(){
    int n=read();
    rep(i, 1, n+1){
        a[i]=read();
        pre[i]=(pre[i-1]+a[i])%mod;
    }
    rep(i, 1, n+1){
        int l=i, r=n+1-i;
        if(l<=r){
            sum=(sum+(pre[r]-pre[l-1]+mod)%mod)%mod;
        }
        else{
            sum=(sum-(pre[l-1]-pre[r]+mod)%mod+mod)%mod;
        }
        ans=(ans+sum*pw(i, mod-2)%mod)%mod; 
    }
    printf("%lld", ans);
    return 0;
}

计算题

原题链接

思路

对于本题,首先你需要能够快速的计算出一个带单次不匹配的回文串,有两种方法。
方法一:
在同一hash规则下,从先往后hash和从后往前hash的值相等即可保证该串是回文,如果不相等,可以将该字符串切分成两段,如果存在一段相等一段不等,那么继续判断不等的那一串,如果都不相等,那么他一定会存在两个不相等的地方。
方法二:
通过二分将第前缀相等的地方计算出来,然后跳过不等的字符,继续判断剩下的字符串是否相等。
当你知道上述方法之后答案基本上就计算出来了。
然后分类讨论一下,如果一个串是回文串,且他是一个偶数长度串,那么他无法随意改变,字符串本质不变,贡献1。
如果是回文串,并且他是一个奇数回文串,那么我们可以更改中间的字符,贡献2.
如果一个串是一个只有一个位置不等的伪回文串,那么我们可以修改不等的地方,左右两遍都可以修改成对方的样子,贡献2。

算法标签

思维 数学 哈希

代码

#include<bits/stdc++.h>
using namespace std;
const int base=2333;
unsigned long long ans,bs[2000001],h[2000001];
int m,n;
char s[2000001];
inline unsigned long long get_hsh(int l,int r)
{
    return h[r]-h[l-1]*bs[r-l+1];
}
inline int query(int x,int y)
{
    int ret=0,l=1,r=min(x,n-y+1);
    while(l<=r)
    {
        int mid=l+r>>1;
        if(get_hsh(n*2-x+1,n*2-x+1+mid-1)==get_hsh(y,y+mid-1))
            ret=mid,l=mid+1;
        else
            r=mid-1;
    }
    return ret;
}
int main()
{
    scanf("%d%s",&n,s+1);
    for(int i=1;i<=n;++i)s[n*2-i+1]=s[i];
    m=n<<1;
    bs[0]=1;
    for(int i=1;i<=m;++i)
        bs[i]=bs[i-1]*base,h[i]=h[i-1]*base+s[i];
    for(int i=1;i<=n;++i)
    {
        if(i&1)
        {
            int mid=i+1>>1;
            int len1=query(mid,mid);
            if(len1==mid)
                ans+=26;
            else
            {
                int len2=query(mid-len1-1,mid+len1+1);
                if(len1+1+len2==mid)
                    ans+=2;
            }
        }
        else
        {
            int mid1=i>>1,mid2=i+2>>1;
            int len1=query(mid1,mid2);
            if(len1==mid1)
                ++ans;
            else
            {
                int len2=query(mid1-len1-1,mid2+len1+1);
                if(len1+1+len2==mid1)
                    ans+=2;
            }
        }
    }
    printf("%lld",ans);
}
战绩

在这里插入图片描述

战果

在这里插入图片描述

原创不易
转载请标明出处
如果对你有所帮助 别忘啦点赞支持哈
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

飞滕人生TYF

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值