牛客周赛 Round 108

赛时F题差一个样例没过....

A. ICPC World Finals

题目描述

在大学生算法竞赛的社区里流传着这样一句话:"如果一个队的队长四级没过并且挂了科,那么他就 WF 了。"(也就是说如果一个队长没通过英语四级考试(CET4)并且存在必修课不及格的记录,则他就能晋级 ICPC World Finals,也就是世界总决赛,简称 WF。)

当然,这显然是一句玩笑话。通过夸张的描述体现出晋级 WF 需要超乎常人的努力,以至于可能会影响到很多别的学业。

但小苯身为队长仍然对此深信不疑,因此他给定你:他的四级成绩 sss,以及作为衡量指标的三门必修课成绩 s1,s2,s3,请你来判断一下,依照上述的传言,他能否晋级 WF。

注意:四级通过的分数线为:425 分,必修课及格的分数线为:60 分。
解题思路:按题目要求来写

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
void solve(){
    int s,s1,s2,s3;
    cin>>s>>s1>>s2>>s3;
    if(s<425 && (s1<60||s2<60||s3<60)) { cout<<"YES"<<'\n'; return; } 
    else cout<<"NO"<<'\n';
}
int main(){
    int t=1; 
//     cin>>t;
    while(t--){
        solve();
    }
}

B. 小苯的数字排序

题目描述

小苯有 n 个数字 a1,a2,...,an​,他希望将这些数字按照以下规则排序:

1..​所有偶数排在所有奇数前面;

2.​在规则 1 的基础上,偶数与偶数之间、奇数与奇数之间,都按照数值从小到大的顺序排列。

请你帮他排出一个合理的顺序吧。
解题思路:先排序, 按要求输出偶数排在前面, 奇数排在后面

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
void solve(){
   int n; cin>>n;
   vector<int> a(n);
   for(int i=0;i<n;i++) cin>>a[i];
   sort(a.begin(),a.end());
   for(int i=0;i<n;i++){
       if(a[i] & 1){
            continue;
       }else{
            cout<<a[i]<<" ";
       }
   }
   for(int i=0;i<n;i++){
       if(a[i] & 1){
            cout<<a[i]<<" ";
       }
   }
   cout<<'\n';
}
int main(){
    int t; 
    cin>>t;
    while(t--){
        solve();
    }
}

C.小苯的数字合并

题目描述

小苯有一个长度为 n 的数组 a1,a2,...,an​,他可以对 a 进行任意次"数字合并"操作,具体地,一次数字合并操作描述为:

选择一个下标 i(1≦i<∣a∣)i,将 ai​ 和 ai+1​ 合并为一个数字,结果为两个的和 ai+ai+1。合并后数组长度减 1,下标按新数组重新编号。

现在小苯可以进行任意次上述操作,他想知道他可以得到多少种本质不同结果数组,请你帮他数一数吧。换句话说,在可以进行任意次操作的情况下,所有可能得到 的数组 a 有多少种本质不同的模样。由于答案可能很大,请将答案对 998 244 353 取模后输出。

小苯认为两个数组 a,b 本质不同,当且仅当以下两个条件至少满足其中之一:

,∙两个数组的长度不同;

,∙两个数组的长度相同,但存在至少一个 i(1≦i≦∣a∣),使得 ai≠bi。
解题思路:相邻数组元素合并后, 长度减1, n个元素, n-1个间隙, 每个间隙选/不选, 一共2^n-1种选法, 最后答案2^n-1 % 998 244 353

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int M = 998244353;
using ll = long long;
ll fun(ll x,ll n){
    ll res=1;
    while(n){
        if(n & 1) res=res*x%M;
        x=x*x%M;
        n>>=1;
    }
    return res;
}
void solve(){
    int n; 
    cin>>n;
    vector<int> a(n);
    for(int i=0;i<n;i++) cin>>a[i];
    cout<<fun(2,n-1)<<'\n';
}
int main(){
    int t; 
    cin>>t;
    while(t--){
        solve();
    }
}

D. 小苯的子序列权值

题目描述

小苯有一个长度为 n 的序列 a1,a2,...,an,他认为一个序列的权值为:序列中所有数字的++按位与++ 。

现在小苯想知道所有的非空(显然一共 2^n−1 个)的++子序列++ 中,有多少个子序列的权值是偶数,请你帮他算一算吧。由于答案可能很大,请将答案对 998244353 取模后输出。

【名词解释】
++按位与++ (Bitwise AND):对两个整数的二进制表示按位进行与运算。如果您需要更多位运算相关的知识,可以参考 OI-Wiki的相关章节

:从原序列中删除任意个(可以为零、可以为全部)元素得到的新序列。
解题思路:全部子序列的个数 - 奇数子序列的个数 = 偶数子序列的个数

权值是奇数的子序列中一定全是奇数

答案就是 2^n - 2^cnt

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int M = 998244353;
using ll = long long;
ll fun(ll x,ll n){
    ll res=1;
    while(n){
        if(n & 1) res=res*x%M;
        x=x*x%M;
        n>>=1;
    }
    return res;
}
void solve(){
    int n; 
    cin>>n;
    vector<int> a(n);
    int cnt=0;
    for(int i=0;i<n;i++) { cin>>a[i];  if (a[i] & 1) { cnt++; } }
    ll b = fun(2,n); ll c= fun(2,cnt);
    cout << (b - c + M)%M << '\n';
}
int main(){
    int t; 
    cin>>t;
    while(t--){
        solve();
    }
}

E. 小苯的有趣数

题目描述

小苯发现了一些「有趣的」数字,即:数字本身是个++完全平方数++ ,且其各个数位之和也是个++完全平方数++ !例如 2025 本身就是个完全平方数,同时其各个数位之和:2+0+2+5=9 也是个完全平方数,因此小苯认为 20252 就是个「有趣的」数字。

现在小苯有一个长度为 n 的序列 a1,a2,...,an​,他可以对 a 做任意次以下操作:

,∙选择两个不同的下标 i,j(1≦i,j≦n; i≠j),满足 ai≧2,随后将 ai​ 减去 1,aj加上 1。

他想知道,自己至多可以把 a 中多少个数字变成「有趣的」数字,请你帮他算一算吧。

【名词解释】
++完全平方数++:一个数如果可以表示为某个整数的平方,那么这个数就是完全平方数。前十个完全平方数是 0,1,4,9,16,25,36,49,64,81。
解题思路:

fun_1: 判断数字 x 是不是完全平方数

fun_2:判断各个数位之和是不是完全平方数

把每个元素至少保留 1,把总和减去 n 记作 S',这些多余的数必须分配到若干个位置上。我们要判断能不能把所有 n 个位置都变成「有趣数」。因为 1 本身是「有趣数」,所以可以把任意位置设为 1。

要让所有 n 个位置都是「有趣数」,等价于把 S' 表示成若干个 (有趣数 − 1) 的和(每个选项可重复),且所用项数 ≤ n(因为最多只有 n 个位置可以承载正的代币,其余位置用 1 填充)。如果能做到,答案就是 n;否则(但 n>=2)我们至少可以把 n-1 个位置设为 1,把全部 S' 堆到一个位置上,所以答案是 n-1。n==1 要特殊处理(不能有 i≠j 的操作),直接看原数是否「有趣」

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int M = 998244353;
const int N = 1e9+7;
using ll = long long;
ll fun(ll x,ll n){
    ll res=1;
    while(n){
        if(n & 1) res=res*x%M;
        x=x*x%M;
        n>>=1;
    }
    return res;
}
bool fun_1(int x) {
    if (x < 0) return false;
    int r = (int)floor(sqrt((double)x));
    while ((ll)r * r < x) ++r;
    while ((ll)r * r > x) --r;
    return (ll)r * r == x;                   
}
bool fun_2(int x){
    int sum=0;
    while(x){
        sum+=x%10;
        x/=10;
    }
    return fun_1(sum);
}
void solve(){
    int n; 
    cin>>n;
    vector<int> a(n);
    int sum = 0;
    for(int i=0;i<n;i++) { cin>>a[i]; sum+=a[i]; }
    if(n == 1) { if(fun_1(a[0]) && fun_2(a[0])) { cout<<1<<'\n'; }
    else cout<<0<<'\n';  return;     }
    if(sum-n<0) {
        cout<<0<<'\n';
        return;
    }
    if(sum-n == 0) { cout<<n<<'\n'; return; }
    vector<int> b;
    for(int j=0;(ll)j*j<=sum-n+1;j++){
        int x=j*j;
//         if(fun_1(x) && fun_2(x)){
//             if(x > 1) b.push_back(x-1);
//         }
        if(x==0) continue;
        if(fun_2(x)){
            if(x>1&&x-1<=sum-n) b.push_back(x-1);
        }
    }
    
    if(b.size()==0) { cout<<n-1<<'\n'; return; }
    vector<int> dp(sum-n+1,N);
    dp[0]=0;
    for(int x: b){
        for(int i=x;i<=sum-n;i++){
            if(dp[i-x]!=N){
                dp[i]=min(dp[i],dp[i-x]+1);
            }
        }
    }
    if(dp[sum-n]<=n) cout<<n<<'\n';
    else cout<<n-1<<'\n';
}
int main(){
    int t; 
    cin>>t;
    while(t--){
        solve();
    }
}

F. AND VS MEX

题目描述

小苯有一个初始为空的可重 数字集合 S 和一个长度为 n 的序列 a1,a2,...,an​。现在他可以进行任意次以下操作,给 S 中加入一些元素,具体的:

,∙他可以任选 a 中的任意个(也可以不选,此时 and⁡ 视为 0)数字,将这些数字的 and(按位与)加入集合 S。

他可以做任意次上述操作,请问 S 的 mex⁡ 最大可以达到多少。

【名词解释】

and:即按位与(Bitwise AND),指对两个整数的二进制表示按位进行与运算。如果您需要更多位运算相关的知识,可以参考 OI-Wiki的相关章节

mex:整数数组的 mex 定义为没有出现在数组中的最小非负整数。例如,mex⁡(1,2,3)=0、mex⁡(0,2,5)=1。
解题思路:

记 S 为允许加入的所有按位与结果的集合(你每次都可以选任意子集,把它们的 AND 放进集合)。我们想要最大化 mex(S),等价于找到最大的 k 使得所有 0,1,...,k−1 都能由某个非空子集的 AND(注意 0 可以来自"空选")得到。

对某个 x>0,存在一个(非空)子集的 AND 恰好为 x 的充要条件是:

考虑集合 Tx={ai∣(ai&x)==x}(也就是二进制上"包含" x 的数);必须有至少一个元素属于 Tx,且

把 Tx​ 中所有元素做位与(即对所有满足 (ai&x)==x 的 ai 做 AND),结果恰好等于 x。

------ 证明要点:如果对 Tx​ 的所有元素在某一位上都是 1,那么任选子集也无法把该位变成 0;反之,对于每个 x 中为 0 的位只要存在某个 ai∈T 在该位为 0,选这些元素的并集就能把所有"额外位"变成 0,从而得到 x。

因此我们只要能对每个掩码 x 快速算出 "所有 v(值)且 (v&x)==x 的值的位与",就能判定 x 是否可得

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
const int M = 998244353;
using ll = long long;
ll fun(ll x,ll n){
    ll res=1;
    while(n){
        if(n & 1) res=res*x%M;
        x=x*x%M;
        n>>=1;
    }
    return res;
}
void solve(){
    int n;
    cin >> n;
    vector<int> a(n);
    int mx=-1;
    for(int i=0;i<n;i++) { 
        cin >> a[i]; 
        mx = max(a[i],mx); 
    }
    if(n==1&&a[0]==0) { cout<<1<<'\n'; return; } 
    int b=1;
    while((1<<b) <= mx) b++;
    int c = 1<<b;
    int d = c-1;
    vector<int> e(c,d);
    for(int x: a){
        e[x] &= x;
    }
    for(int i=0;i<b;i++){
        for(int j=0;j<c;j++){
            if (((j>>i)&1) == 0){
                e[j] &= e[j|(1<<i)];
            }
        }
    }
    int mex=1;
    while(mex<c && e[mex]==mex) mex++;
    cout<<mex<<'\n';
}
int main(){
    int t; 
    cin>>t;
    while(t--){
        solve();
    }
}

感谢大家的点赞和关注,你们的支持是我创作的动力!

详细讲解后续再写

相关推荐
小刘的AI小站4 小时前
leetcode hot100 二叉搜索树
算法·leetcode
自信的小螺丝钉4 小时前
Leetcode 876. 链表的中间结点 快慢指针
算法·leetcode·链表·指针
红豆怪怪4 小时前
[LeetCode 热题 100] 32. 最长有效括号
数据结构·python·算法·leetcode·动态规划·代理模式
愚润求学5 小时前
【贪心算法】day6
c++·算法·leetcode·贪心算法
AI 嗯啦5 小时前
计算机的排序方法
数据结构·算法·排序算法
沐怡旸5 小时前
【底层机制】右值引用是什么?为什么要引入右值引用?
c++·面试
l12345sy5 小时前
Day23_【机器学习—聚类算法—K-Means聚类 及评估指标SSE、SC、CH】
算法·机器学习·kmeans·聚类·sse·sc·ch
_Coin_-5 小时前
算法训练营DAY58 第十一章:图论part08
数据结构·算法·图论
scx201310046 小时前
P13929 [蓝桥杯 2022 省 Java B] 山 题解
c++·算法·蓝桥杯·洛谷