
赛时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();
}
}
感谢大家的点赞和关注,你们的支持是我创作的动力!
详细讲解后续再写