
赛时F题差一个样例没过....
A. ICPC World Finals
题目描述
在大学生算法竞赛的社区里流传着这样一句话:"如果一个队的队长四级没过并且挂了科,那么他就 WF 了。"(也就是说如果一个队长没通过英语四级考试(CET4)并且存在必修课不及格的记录,则他就能晋级 ICPC World Finals,也就是世界总决赛,简称 WF。)
当然,这显然是一句玩笑话。通过夸张的描述体现出晋级 WF 需要超乎常人的努力,以至于可能会影响到很多别的学业。
但小苯身为队长仍然对此深信不疑,因此他给定你:他的四级成绩 sss,以及作为衡量指标的三门必修课成绩 s1,s2,s3,请你来判断一下,依照上述的传言,他能否晋级 WF。
注意:四级通过的分数线为:425 分,必修课及格的分数线为:60 分。
解题思路:按题目要求来写
#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 的基础上,偶数与偶数之间、奇数与奇数之间,都按照数值从小到大的顺序排列。
请你帮他排出一个合理的顺序吧。
解题思路:先排序, 按要求输出偶数排在前面, 奇数排在后面
#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
#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
#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:判断各个数位之和是不是完全平方数
例如有长度为5的序列
1 4 6 8 9 tot_sum=28tot_sum-5=23
23分配到5个位置, 能否形成5个有趣数
如果不能, 就4个位置为1, tot_sum 添加到第5个位置
特殊的当序列长度等于1时, 直接特判这个数是不是有趣数所有满足条件的j*j, 将j*j-1存到 数组 b中
将数组b中的元素分配到原始的5个位置, 就会形成有趣数上面我们把总和拆成 n 个数。
为了保证每个位置 至少是 1(因为不能放 0),先在每个位置放一个 1。
这样:
已经放了总和 n;
还差 sum - n = S 需要分配。
所以现在问题变成:
把 S 拆成若干份,每一份的大小必须是某个“有趣数的额外消耗”什么是 “有趣数的额外消耗”?
如果我们把某个位置从 1 提升到 j²,那多消耗的就是 j² - 1
比如:位置本来是 1,我想让它变成 4=2²,那这格子要多拿 4-1=3。
如果想让它变成 9=3²,就要多拿 9-1=8。
所以候选集合是:b={j^2-1|j^2-1<=S}对态规划部分:
dp[i] = 凑出剩余和 i 时,最少需要用多少个有趣数的位置。
注意:这里的“有趣数”不是直接指 j² 本身,而是说 选择一个位置当作有趣数,需要消耗多少 S 值举个例子:
我想让一个格子变成 9(即 3²),这意味着我从 S 里拿走 8(=9-1),同时这就贡献了 1 个有趣数。
如果我想让两个格子都变成 9,那我得从 S 里拿 16,同时这贡献了 2 个有趣数。所以, 此时dp问题就转换成:
“物品重量” = j² - 1
“物品价值” = 1(即一个有趣数的数量)
“目标重量” = S
求最少价值能凑出目标重量。
为什么最后要看 dp[sum-n] <= n
dp[S] = 把 S 分配出去,最少需要多少个位置变成有趣数。
如果这个最小值 ≤ n,说明在 n 个位置里足够塞下这些有趣数,可以让所有位置都是有趣数 → 输出 n。
否则,说明即使用最优方式也需要超过 n 个有趣数,不可能每个位置都填好,只能退一步,答案是 n-1。
这样解释了为啥上面dp[i], 定义为最少需要.... , 如果定义最多就有可能超过n, 导致有趣数变为 n-1dp[i] 里的“最少需要的有趣数个数”,就是 在消耗额外值 i 时,最少需要多少个位置提升为有趣数。
dp其实就是完全背包:
若 i-x 可达(dp[i-x] != N),那么可以通过再加一个 x 来达到 i,此时所需个数为 dp[i-x] + 1。
dp[i] = min(dp[i], dp[i-x] + 1):取最少值
#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。
解题思路:
有一个长度为n的序列a, 你可以执行任意次以下操作,
从 a中任选数字进行&,操作,然后将&后的数字加入到集合S中
多次操作后
找出集合S中的最小非负整数0~x-1能被凑出来, 那么答案就是x, 如果x 也能被凑数来, 那答案就是x+1
check(x) : x能否由a的某个子序列and凑出来
=> x的超集全and 起来 = xeg:
x: 11011
超集: 11(0/1)11
x二进制中为1的地方超集也为1, x为0的地方超集可以0, 也可以为1
因此, x and 超集 = x超级>=x , 越and越小, 因此选上所有超集, 如果选了非超集的数, 结果一定不等于x
高维前缀和
fi: i的超集and的结果
只需枚举恰好多一个bit的超集x: 00110110
多一个bit, 就类似于:
y: 10110110
#include <bits/stdc++.h>
using namespace std;
using ll =long long;
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(mx, a[i]);
}
ll m = max<ll>(mx, n);
int b = 0;
while((1LL << b) <= m) 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)];
}
}
}
for(int mex=1; mex<=n; mex++){
if(e[mex] > mex){
cout << mex << '\n';
return;
}
}
cout << n+1 << '\n';
}
int main(){
int t;
cin >> t;
while(t--){
solve();
}
}
感谢大家的点赞和关注,你们的支持是我创作的动力!
1129

被折叠的 条评论
为什么被折叠?



