牛客周赛66

https://ac.nowcoder.com/acm/contest/93847)(比赛链接)

目录

A.小苯吃糖果

B.小苯的排列构造

C.小苯的最小整数

D.小苯的蓄水池(easy)

E.小苯的蓄水池(hard)

F.小苯的字符提前


A.小苯吃糖果

1.思路

        最大的值和剩下的两个值的和比较,取较大的那个输出即可

2.代码

#include <bits/stdc++.h>
#define int long long 
using namespace std;

signed main(){
    int x,y,z;
    cin>>x>>y>>z;
    int sum = x+y+z;
    int max1 = max(x,y);
    max1 = max(max1,z);
    if(max1>sum-max1){
        cout<<max1<<endl;
    }else{
        cout<<sum-max1<<endl;
    }
    return 0;
}

B.小苯的排列构造

1.思路

        输出长度为n,首项为n,公差为1的递减序列即可

2.代码

#include <bits/stdc++.h>
#define int long long
using namespace std;

void solve(){
    int n;
    cin>>n;
    for(int i=n;i>=1;i--){
        cout<<i<<" ";
    }
    cout<<endl;
}

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

C.小苯的最小整数

1.思路

        通过字符串进行输入,将所有情况都遍历,进行比大小,不断更新最小值。因为数值比较大,将字符串转化为长整型的时候选择用stoll函数进行转化,这里的LLONG_MAX是长整型最大值

2.代码

#include <bits/stdc++.h>
#define int long long
using namespace std;

void solve(){
    string s;
    cin>>s;
    int min1 = LLONG_MAX;
    for(int i=0;i<s.length();i++){
        string re = s.substr(i+1,s.length());
        re += s.substr(0,i+1);
        int tt = stoll(re);
        min1 = min(min1,tt);
    }
    cout<<min1<<endl;
}

// 4562827
// 2827456
// 2745628

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

D.小苯的蓄水池(easy)

1.思路

        数组a保存原始数据,数组b保存更新的结果。利用vis数组来判断某个位置是否更新过,因此对于每次更新操作,通过利用vis数组,来找到受到影响的区间,并通过累加这个区间和,除以平均数,然后对受到影响的这个区间都进行更新,将更新的结果保存在b数组中。对于每次查询,输出b[i]即可

2.代码

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int N = 2e3+7;
double a[N],b[N];
int vis[N];

void solve(int n){
    int op;
    cin>>op;
    if(op==1){
        int l,r;
        cin>>l>>r;
        double sum = 0;
        int count = 0;
        int left=l,right=r;
        for(int i=l;i>=1;i--){
            if(vis[i]==1){
                left = i;
            }else{
                break;
            }
        }
        for(int i=r;r<=n;i++){
            if(vis[i]==1){
                right = i;
            }else{
                break;
            }
        }
        for(int i=left;i<=right;i++){
            sum+=a[i];
            count++;
        }
        sum/=count;
        for(int i=left;i<=right;i++){
            b[i] = sum;
            vis[i] = 1;
        }
    }else if(op==2){
        int i;
        cin>>i;
        printf("%.10lf\n",b[i]);
    }
}

signed main(){
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        double t;
        cin>>t;
        a[i] = t;
        b[i] = t;
    }
    while(m--){
        solve(n);
    }
}

E.小苯的蓄水池(hard)

1.思路

        利用并查集对合并的区域进行维护,每次拆除挡板时,都向右进行合并,并将所有合并的区域的数值都存在最右边的那个父亲节点上,包括区间的长度。查询时,直接寻找其父亲节点,进行输出即可

2.代码

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int N = 2e5+7;
double a[N],fa[N],sz[N];    // a[i]表示合并的区段和,fa[i]表示i的父亲节点,sz[i]表示合并的区段长度

int find(int x){    // 查找父亲节点
    if(fa[x]!=x){   // 如果自身不是自己的父亲则递归查找父亲节点,同时更改自己的父亲为对应的节点
        return fa[x] = find(fa[x]);
    }else{
        return x;   // 自身是自己的父亲,直接返回即可
    }
}

void merge(int l, int r){
    int u = find(l);    // 查找所要合并的两个水池的父亲节点
    int v = find(r);
    if(u==v){       // 相同直接退出
        return ;
    }
    if(v<u){        // 保证向大的值,即右侧进行合并
        swap(v,u);
    }
    a[v]+=a[u];     // 区段和累计
    sz[v]+=sz[u];   // 区段长度累计
    fa[u] = v;      // 合并,更新父亲节点
}

void solve(){
    int op;
    cin>>op;
    if(op==1){
        int l,r;
        cin>>l>>r;
        while(l<r){
            merge(l,l+1);   // 区间合并
            l = find(l);    // l能直接跳转到其父亲的位置,因为之间的位置一定是合并过的
        }
    }else if(op==2){
        int i;
        cin>>i;
        int fa = find(i);   
        printf("%.10lf",a[fa]/sz[fa]);
    }
}

signed main(){
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        fa[i] = i;  // 默认每个水池位置自己都是自己的父亲
        sz[i] = 1;  // 默认每个区段长度都为1
    }
    while(m--){
        solve();
    }
}

3.官方题目讲解(本周官方题解讲解的很好很详细,强烈建议去看看!)https://www.bilibili.com/video/BV1o3S2YREhu?spm_id_from=333.788.videopod.episodes&vd_source=b0cde2c7518af1c65116188091dadb6d&p=3icon-default.png?t=O83Ahttps://www.bilibili.com/video/BV1o3S2YREhu?spm_id_from=333.788.videopod.episodes&vd_source=b0cde2c7518af1c65116188091dadb6d&p=3

F.小苯的字符提前

1.思路

        首先确定第k小的Move串的首字符为什么(如b),遍历字符串,统计每个字符的数量,从小到大进行累计,若小于k说明不在当前字符继续累计求和,否则说明第k小的move串就在以当前字符为首的字符集中。nxt数组用于存储当前字符之后的第一个与当前字符不同的字符的位置。最后从字符串从后往前进行处理,个人认为,因为从后往前处理能够保证每次能直接在双端队列中添加元素。因为这样保证后面的字符已经被处理完,文字难以叙述,强烈建议观看官方题解讲解。

2.代码

#include <bits/stdc++.h>
#define int long long
using namespace std;

int cnt[27];    // 统计字符串中每个字符的个数

void solve(){
    for(int i=0;i<26;i++){
        cnt[i]=0;
    }
    int n,k,sum=0,index=-1;
    cin>>n>>k;
    string s;
    cin>>s;
    for(auto c:s){
        cnt[c-'a']++;
    }
    for(int i=0;i<26;i++){
        if(sum+cnt[i]<k){   // 说明不在当前字符位置
            sum+=cnt[i];
        }else{
            index = i;  // 最终结果的move串的首字符即为当前字符
            break;
        }
    }
    vector<int>nxt(n);  // 存储当前字符之后的第一个与当前字符不同的字符的位置
    for(int i=n-1;i>=0;i--){
        if(i<n-1&&s[i]==s[i+1]){
            nxt[i] = nxt[i+1];
        }else{
            nxt[i] = i+1;
        }
    }
    deque<int>q;    // 存储从小到大排序的结果字符的位置
    for(int i=n-1;i>=0;i--){
        if((s[i]-'a')!=index){
            continue;
        }
        int x = nxt[i];
        if(x<n&&s[i]<s[x]){     // 当前字符小于s[nxt[i]],说明比之后的任意一个move串都大
            q.emplace_back(i);
        }else{
            q.emplace_front(i); // 当前字符大于s[nxt[i]],说明比之后的任意一个move串都小
        }
    }
    for(int i=1;i<k-sum;i++){
        q.pop_front();      // 找到第k小的move串的对应的首字符的位置
    }
    cout<<s[q.front()]<<s.substr(0,q.front())<<s.substr(q.front()+1)<<endl;
}

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

G.小苯的数位MEX

好难不会

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值