2024年1月29日牛客周赛round 30

B、小红的正整数

听了讲解有了另一种方式,适用于更大范围。在这里补充:利用桶排序。

桶排序是假设待排序的一组数均匀独立的分布在一个范围中,并将这一范围划分成几个子范围(桶)。

然后基于某种映射函数f ,将待排序列的关键字 k 映射到第i个桶中 (即桶数组B 的下标i) ,那么该关键字k 就作为 B[i]中的元素 (每个桶B[i]都是一组大小为N/M 的序列 )。

接着将各个桶中的数据有序的合并起来 : 对每个桶B[i] 中的所有元素进行比较排序 (可以使用快排)。然后依次枚举输出 B[0]….B[M] 中的全部内容即是一个有序序列

#include<bits/stdc++.h>

using namespace std;
int tong[10];//数组下标代表0~9数字,数组的值代表每个数字出现的次数
int main(){
    string s;
    cin>>s;
    for(auto i:s) tong[i-'0']++;//存入每个数字出现的次数
    int i;
    for(i=1;i<=9;i++){//跳过0,把最小的非零正整数输出
        if(tong[i]) break;
    }
    cout<<i;
    tong[i]--;//让这个数出现次数减一
    for(i=0;i<=9;i++){
        while(tong[i]--) cout<<i;
    }
    
}

C、小红的构造回文

 

回文串:从左往右读和从右往左读一样

思路:可以从中间分开,从左边找到两个不同位置且字符不同,交换一下;右侧在对应位置也进行交换,最后也是回文串。如果从中间开始,左侧元素相同,这就意味着无论怎样交换都是一样的。

关于找对称的思路:对称问题取决于下标是从0~n-1还是1~n;以0~n-1为例:

0与n-1相对;1与n-2相对;2与n-3相对······,他们的和始终为i-1,则i与n-1-i相对。

#include<bits/stdc++.h>

using namespace std;

int main(){
    int i;
    string s;
    cin>>s;
    int len=s.length();
    for(i=1;i<len/2;i++){//遍历左半边
        if(s[i]!=s[i-1]){
            swap(s[i],s[i-1]);
            swap(s[len-1-i],s[len-1-(i-1)]);
            cout<<s;
            return 0;
        }
    }
    cout<< -1;
    
}

D、小红的整数操作

思路: 基本思路:首先利用除法将x,y除到最小,然后再进行乘法,将x,y增大至[l,r]的区间里去。通过将改变小的x,y确定范围,寻找最大乘多少,最小乘多少,确定答案。

(前提:若x<y),最小情况是:x*a>=l,最大情况是y*a<=r。

扩展:使用 __gcd( int x1, int x2 ) 函数可以高效、迅速得到x1, x2两个数的最大公因数。这个函数在<algorithm>库中

数学法:

#include<bits/stdc++.h>

using namespace std;

int gcd1(int x,int y){//手动求最大公约数
    if(x%y==0) return y;
    return gcd1(y,x%y);
}

int main(){
    int x,y,l,r;
    cin>>x>>y>>l>>r;
    int g=gcd(x,y);//求最大公约数
    x/=g;
    y/=g;
    if(x>y) swap(x,y);
    /*
    通过举例推数学公式:
    要求:x*a>=l;y*a<=r;
    例:设x或y为3,l和r为10;
    故当求a时,l/x需要向上取整,r/y需要向下取整
    */
    int a=l/x+(l%x!=0);
    int b=r/y;
    cout<<max(0,b-a+1);
    return 0;
}

二分法有错误,但没看出来,只过了80%的数据,有能力了回头看看

#include<bits/stdc++.h>

using namespace std;

int main(){
    int x,y,l,r;
    cin>>x>>y>>l>>r;
    int g=gcd(x,y);//求最大公约数
    x/=g;
    y/=g;
    if(x>y) swap(x,y);
    /*
    要求:x*a>=l;y*a<=r;
    */
    int xi=0,da=l;
    while(xi<da){
        int mid=xi+da>>1;
        if(x*mid>=l) da=mid;
        else xi=mid+1;
    }
    int x2=0,d2=r;
    while(x2<d2){
        int mid2=x2+d2+1>>1;
        if(y*mid2<=r) x2=mid2;
        else d2=mid2-1;
    }
    printf("%d",x2-xi+1);
    return 0;
}

E、小红树上染色

这是个经典题,特此放在这里作为记录。

相当于取若干个不相邻的数有多少种不同的取法 ,(树形DP问题),类似于不相邻取数牛客竞赛可搜。

#include<bits/stdc++.h>

using namespace std;
int mod=1e9+7;
long long dp[101010][2];//dp[i][0]代表i号节点不染红,dp[i][1]代表i号结点染红
//建树方法
vector<int> g[202020];
int vis[202020];

void dfs(int x,int pr){//pr代表父代
    dp[x][0]=dp[x][1]=1;
    for(auto i:g[x]){
        if(i==pr) continue;//当遍历的邻点正好是父亲就只能向下
        dfs(i,x);
        
        dp[x][0]=dp[x][0]*dp[i][1]%mod;//如果点不染红,那他的孩子必须染红
        dp[x][1]=dp[x][1]*(dp[i][0]+dp[i][1])%mod;//如果点染红,那么他的孩子可染可不染
    }
}
int main(){
    int n,i;
    cin>>n;
    for(i=1;i<n;i++){
        int x,y;
        cin>>x>>y;
        g[x].push_back(y);//把每个点的邻点存到该点的数组里
        g[y].push_back(x);
    }
    dfs(1,0);//默认1号点为根,且根没有父代,所以为0
    cout<<(dp[1][0]+dp[1][1])%mod;
}

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值