牛客周赛 Round 38 (A,B,C,D,E,F)

文章描述了一系列编程问题,包括使用C++解决小红的整数自增、抛弃后缀的判断、字符串构造形成回文、平滑值插值优化序列、等比数列最长子序列和回文子数组查询,涉及算法、数据结构和高精度计算。
摘要由CSDN通过智能技术生成
A.小红的正整数自增
#include <iostream>
#include<algorithm>
using namespace std;

void Solved() {

    int x;
    cin>>x;
    
    int t=x%10;
    if(t==0) cout<<0<<endl;
    else cout<<10-t<<endl;
}

int main()
{

    int t;
    t=1;

    while(t--) {
        Solved();
    }
    return 0;
}
B.小红的抛弃后缀

这题有两种做法一个是结论,一个是直接用高精度思想直接模拟。

一,方法一:结论

结论就是和3这个类似,当一个数的数位和可以被9整除那么这个数就能被9整除如:18。

#include <iostream>
#include<algorithm>
using namespace std;

void Solved() {

    string str;
    cin>>str;

    int cnt=0;
    int sum=0;
 
    for(int i=0;i<str.size();i++){

        sum = (sum +(str[i]-'0'))%9;
        if(sum==0) cnt++;

    }
    
    cout<<cnt<<endl;
}

int main()
{

    int t;
    t=1;

    while(t--) {
        Solved();
    }
    return 0;
}

二,方法二:高精度思想模拟

思路直解根据高精度思想模拟出一个数除以9的计算过程即可

#include <iostream>
#include<algorithm>
using namespace std;
 
 
void Solved() {
 
    string str;
    cin>>str;
 
    ll cnt=0;
//表示余数
    int t=0;
 
//高精度思想
    for(int i=0;i<str.size();i++){
       int num=t*10+str[i]-'0';
       if(num%9==0) cnt++;
       t=num%9;
    }
 
    cout<<cnt<<endl;
}
 
int main()
{
 
    int t;
    t=1;
 
    while(t--) {
        Solved();
    }
    return 0;
}
C.小红的字符串构造

也是两种方法一种是构造全是一样字母结合分求解,一种是根据题目的数据范围k<=n/2,可以联想到aabbccaabbcc这种构造方法。这两个方法都利用了一个性质就是独立贡献互不影响。

如:aabb----->aa是一个回文,bb是一个回文,他们互不干扰。像aabbaa,这个就不行会相互影响。所以我们可以aabbccaabbcc这样构造他们就不会相互影响了。

方法一:

1.思路:我们可以分析 aa aaa aaaa aaaaa ····,他们的回文数量分别是 1 3  6 10  15。他们的回文数量我们会发现是有规律的,因此我们可以打一个表再利用二分来寻找构造的字母。

2.代码:

#include <iostream>
#include<algorithm>
#include<cstring>
#include<set>
#include<stack>
#include<queue>
#include<map>
using namespace std;
 
const int N=1e5+10,M=1e9+10;
 
typedef  long long ll;
typedef pair<int,int> pii;
ll f[N];
 
void Solved() {
 
    ll n,k;
    cin>>n>>k;
 
//打表
//与之对应的是f[i]=x--->i+1为字符串长度,x为回文数量
    ll stance=2;
    f[1]=1;
    for(int i=2;i<=n;i++){
       f[i]=f[i-1]+stance;
       stance++;
    }
 
 
 //保存结果字符串
    string ans;

//用于使字符串中字母随字母表循序变化
    char c;
    int idx=0;

//通过二分来凑出k个回文
    while(k>0){
 
       ll l=0,r=n;
       while(l<r){
           ll mid=(l+r+1)>>1;
           if(f[mid]<=k) l=mid;
           else r=mid-1;
       }

 //减去已贡献的回文数量
       k-=f[l];

//这次要拼接的字符
       c='a'+idx;

//拼接l+1个c
       for(int i=1;i<=l+1;i++) ans+=c;

//字母变化,在26个字母中循环变化
        idx++;
        if(idx>25){
           c='a';
           idx=0;
        }

 //减去已贡献的字符串长度
        n-=l+1;
    }
 
 //当字符串回文数量满足条件,但是字符串长度不够时,在后面随机补字母
    if(k==0&&n>0){

        for(int i=1;i<=n;i++){
            c='a'+idx;
            idx++;
            ans+=c;
            if(idx>25){
                c='a';
                idx=0;
            }
        }

    }
 
    cout<<ans<<endl;
 
}
 
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
 
    int t;
    //cin>>t;
    t=1;
 
    while(t--) {
        Solved();
    }
    return 0;
}

方法二:

1.思路:用aabbcc,这种构造方法来构造,因为题目数据范围限制使得,一定可以构造出。

2.代码:

#include <iostream>
#include<algorithm>
using namespace std;

void Solved() {

    int n,k;
    cin>>n>>k;

//预先定义出需要循环拼接的字母三个字母足以
    string a="abc",b="def";
    string ans;

    for(int i=0,j=0;i<k;i++){

//两个相同的字母贡献一个回文
       ans+=a[j];
       ans+=a[j];

       j=(j+1)%3;
    }

//在字符串长度不够的时候,在后面随机补字母,前提不要贡献回文
    int j=0;
    while(ans.size()<n){
        ans+=b[j];

        j=(j+1)%3;
    }

    cout<<ans<<endl;
}

int main()
{

    int t;
    t=1;

    while(t--) {
        Solved();
    }
    return 0;
}
D.小红的平滑值插值

思路:这题其实我们只要发现一个性质就可了,要使的某一个成为最大值只需要把,比他大的变得小于等于他即可。当时在这题该如何将差距最大值变小呢?举个例子:3 1 5 我们要使差距为2,所以我们只需要在1和5之间插入一个3即可,那么将 5改成6呢?可以在1 6之间插入 3 5。只要保证我插入的数之间两两的差距小于等于2即可。

代码:

#include <iostream>
#include<algorithm>
using namespace std;

const int N=1e5+10,M=1e7+10;

typedef  long long ll;
typedef pair<int,int> pii;

int arr[N];

void Solved() {

    int n,k;
    cin>>n>>k;

    for(int i=1;i<=n;i++) cin>>arr[i];

    int mx=0;
    ll sum=0;
    for(int i=2;i<=n;i++){

        int num=abs(arr[i]-arr[i-1]);
        mx=max(mx,num);

//当你本来就比目标值小就不需要操做了
        if(num<=k) continue;

//利用数学取余向上取整就能很好的计算出他们之间应该插入的数字数量了
        sum+=(num+(k-1))/k-1;
    }

//当差距最大值都比目标值小时,不管值为多少,都只需要插入一个数字即可。
    if(mx<k) sum=1;

    cout<<sum<<endl;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    int t;
    t=1;

    while(t--) {
        Solved();
    }
    return 0;
}
E.小苯的等比数列

思路:因为是等比数列,且数据范围比较小,所以我们完全可以枚举每一个数的公比来求最大长度。再加一点剪枝。

代码:

#include <iostream>
#include<algorithm>
#include<cmath>
using namespace std;

const int N=4e5+10,M=1e7+10;

typedef  long long ll;
typedef pair<int,int> pii;

int arr[N];
int cnt[N];

void Solved() {

    int n;
    cin>>n;

//去重
    set<int> s;

//元素最大值
    int mx=0;
    int ans=0;

    for(int i=1;i<=n;i++){
        int x;
        cin>>x;

        mx=max(mx,x);

        s.insert(x);
        cnt[x]++;

//预先处理一下公比为1的情况
        ans=max(ans,cnt[x]);
    }


//枚举每个元素
    for(auto x:s){

//枚举他所有公比
//当公比为2的时候 2^17>2e5,所以一个公比最多枚举17次,已经很小了。
//x*pow(i,ans-1)----->当你这个公比根据目前最长等比数列长度来计算,超过最大只是就直接pass

       for(int i=2;x*pow(i,ans-1)<=mx;i++){
           
           if(x*i>mx) break;

//当这个元素乘这个公比连第二项都没有,那么后面也就没有枚举的必要了
           if(cnt[i*x]==0) continue;
           
//看他能往后面推多少项
           int temp=0;
           while(x<=mx&&cnt[x]!=0){
               temp++;
               x*=i;
           }
           ans=max(ans,temp);

       }
    }
    cout<<ans<<endl;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    int t;
    t=1;

    while(t--) {
        Solved();
    }
    return 0;
}
F.小苯的回文询问

思路:这题要知道一个性质,就是我们要看一个子数组里面是否是好数组,只需要看他里面有没有两个一样的元素且他们不相邻如 121。那么怎么来判断呢?我们可以用一个last数组来记录一个数字他上一次出现在哪里(下标)。只要在一个区间 [ l,r ] 之间有一个元素的 last>=l 并且他们还不是相邻的,就满足条件。那么怎么快速的判断这个区间是否满足这个条件,其实只要这个区间的最大的last>=l,那么就满足情况。要快速获取一个区间之间的最大值我们可以用线段数来解决。

代码:

#include <iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<set>
#include<stack>
#include<queue>
#include<map>
using namespace std;

const int N=2e5+10,M=1e7+10;

typedef  long long ll;
typedef pair<int,int> pii;

int arr[N];
int last[N];

//下面都是线段树求解最大值模板
struct Node{
    int l,r;
    int mx;
}tr[4*N];

void build(int u,int l,int r){
   if(l==r) tr[u]={l,r,last[l]};
   else{
       tr[u]={l,r};
       int mid=(l+r)>>1;
       build(u<<1,l,mid),build(u<<1|1,mid+1,r);
       tr[u].mx=max(tr[u<<1].mx,tr[u<<1|1].mx);
   }
}

int query(int u,int l,int r){

    if(l<=tr[u].l&&r>=tr[u].r) return tr[u].mx;
    int mid=(tr[u].l+tr[u].r)>>1;

    int temp=0;
    if(l<=mid) temp=max(temp,query(u<<1,l,r));
    if(r>=mid+1) temp=max(temp,query(u<<1|1,l,r));

    return temp;
}

void Solved() {

    int n,q;
    scanf("%d%d",&n,&q);

//map来记录上一次出现这个元素的位置
    map<int,int> mp;

    int x;
    for(int i=1;i<=n;i++){

        scanf("%d",&x);
        
        if(mp.count(x)){
            last[i]=mp[x];
        }
        mp[x]=i;
    }

//先将相邻且相同元素情况给排除掉,比面影响后面线段树求解
    for(int i=n;i>0;i--){
        if(last[i]==i-1){
            last[i]=last[last[i]];
        }
    }

//线段树求解区间最大值模板

    build(1,1,n);

    for(int i=1;i<=q;i++){
        int a,b;
        scanf("%d%d",&a,&b);

        if(query(1,a,b)>=a) printf("YES\n");
        else printf("NO\n");
    }
}

int main()
{
    int t;
    t=1;

    while(t--) {
        Solved();
    }
    return 0;
}

  • 21
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
牛客 a卷2022年第四季度的华为题目中,要求考生设计一种高效的数据结构,能够支持以下几种操作: 1. 添加一个元素 2. 删除一个元素 3. 查找是否存在某个元素 4. 返回元素的总数 该数据结构要求满足空间复杂度较小、时间复杂度较低、能够快速地进行查找和修改等多种操作。 想要编写这样一种数据结构,我们可以参考许多已有的经典算法与数据结构,如二叉树、哈希表、红黑树等,通过综合利用它们的优点来实现这个问题的解决。 例如,我们可以通过哈希表来存储所有元素的值,并在每个哈希链表的元素中再使用红黑树来进行排序与查找。这样,我们既能够轻松地进行元素的添加和删除操作,也能够在查找较大数据范围和数量时保持较高的速度与效率。同时,由于使用了多个数据结构来协同完成这个问题,我们也能够在空间复杂度上适度地进行优化。 当然,在具体设计这个数据结构的过程中,我们还需要考虑一些实践中的细节问题,例如如何避免哈希冲突、如何处理数据丢失与被删除元素所占用的空间等问题,这都需要相应的算法与流程来进行处理。 总体来看,设计这种支持多种操作的高效数据结构,需要我们具备丰富的算法知识和编程实践能力,同时需要我们在具体处理问题时能够将多种算法和数据结构进行有效地结合。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值