AtCoder Beginner Contest 321
B - Cutoff
难度: ⭐⭐
题目大意
一场考试有n门成绩, 最终成绩是减去最大值和最小值后的和; 现在给出n-1门成绩, 问最后一门成绩最少为多少可以让总成绩大于等于m;
解题思路
列举第n门成绩由小到大时其他成绩所要满足的条件即可;
神秘代码
#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
#define endl '\n'
using namespace std;
const int N = 300 + 10, mod = 998244353;
typedef pair<int, int> PII;
int n, m, idx;
signed main() {
cin>>n>>m;
int mx=0,mn=1e9;
for(int i=1;i<n;i++){
int x;
cin>>x;
mx=max(mx,x);
mn=min(mn,x);
idx+=x;
}
if(idx-mx>=m) cout<<0;
else if(m-idx+mx+mn<=mx) cout<<m-idx+mx+mn;
else if(idx-mn>=m) cout<<mx;
else cout<<-1;
return 0;
}
C - 321-like Searcher
难度: ⭐⭐⭐
题目大意
如果一个数字从前往后各位上的数字是递减的, 那么那个数字就被称为321型数字; 个位数也算321型数字; 问从1开始第k个321型数字是多少;
解题思路
本题想要设计算法来求第k个321型数字就比较困难的, 但是通过打表我们发现整数范围内321型数字数量并不算多, 根据其特性, 我们可以用bfs得到所有321型数字;
神秘代码
#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
#define endl '\n'
using namespace std;
const int N = 2e5 + 10, mod = 998244353;
typedef pair<int, int> PII;
int n, m, idx;
int d[N];
void bfs(){
queue<int> q;
for(int i=1;i<=9;i++){
q.push(i);
}
while(q.size()){
int t =q.front();
q.pop();
d[++idx]=t;
for(int i=0;i<t%10;i++){
int x=t*10+i;
q.push(x);
}
}
}
signed main() {
bfs();
cin>>n;
cout<<d[n];
return 0;
}
D - Set Menu
难度: ⭐⭐
题目大意
菜单上有n种汉堡和m种薯条, 其价格为Ai和Bi; 我们需要把一个汉堡和一个薯条组成一个套餐; 如果该汉堡和薯条的价值和Ai + Bi大于p, 那么该套餐的价格就是p; 否则价格就是Ai + Bi; 请问所有可能的套餐组合的价格总和是多少;
解题思路
因为当Ai + Bi大于p时, 价格就设为p, 所以我们可以把薯条的价格从小到大排序, 对于每种汉堡, 我们用二分找到薯条中第一个与其价格相加大于等于p的; 在这个薯条之前的价格和可以用前缀和来算, 后面的就可以都用p来算; 注意可能薯条中没有与汉堡价格相加大于等于p的, 所以我们的右边界要多出一个表示没找着;
神秘代码
#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
#define endl '\n'
using namespace std;
const int N = 2e5 + 10, mod = 998244353;
typedef pair<int, int> PII;
int n, m, idx;
int a[N],b[N],bs[N];
signed main() {
int p;
cin>>n>>m>>p;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=m;i++) cin>>b[i];
sort(b+1,b+1+m);
for(int i=1;i<=m;i++) bs[i]=bs[i-1]+b[i];
for(int i=1;i<=n;i++){
int x=a[i];
int l=1,r=m+1;
while(l<r){
int mid=l+r>>1;
if(b[mid]+x<p) l=mid+1;
else r=mid;
}
idx = idx+p*(m-l+1)+bs[l-1]+(l-1)*x;
}
cout<<idx;
return 0;
}
E - Complete Binary Tree
难度: ⭐⭐⭐⭐
题目大意
给定一颗二叉树, 设根节点是n, 那么其左孩子就是2 * n, 右孩子是2 * n+1; 这棵树的根节点是1; 给出q次询问, 每次询问给出三个参数n, x, k; 表示这颗二叉树一共有n个节点, 请问和节点x相距k条边的节点数量;
解题思路
因为节点数量n的范围太大, 暴力遍历肯定是不可能的; 我们先看从节点x往下找的时候, 通过观察二叉树我们发现, 所有与x相距k条边的节点都在同一层上, 并且是连续的; 所以我们可以去确定这一层中满足条件区间的左端点和右端点; 左端点好找, 我们需要让x * 2重复k遍就可以到达该层; 右端点则是x * 2 + 1; 注意如果最终左端点大于n, 则说明我们找的那一层不存在; 如果右端点大于n, 则说明这一层就是二叉树的最后一层, 并且该二叉树不是一颗满二叉树, 这时候我们直接让r=n即可, n一定就在那一层;
这样我们就找到了从节点x向下找距离其k条边的所有结点; 然后我们开始往上走, 用x / 2就可以得到x的祖先, 对于x的第p个祖先, 我们只需要向下找距离其k-p条边的点就可以了; 注意从祖先往下走时可能会找到之前已经找过的节点, 所以在计算完x的第p个祖先时, 要将其减去x的第p-1个祖先往下距离其k-p-1条边的节点数量就可以完成去重了; 复杂度是(n*logn2);
神秘代码
#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
#define endl '\n'
using namespace std;
const int N = 2e5 + 10, mod = 998244353;
typedef pair<int, int> PII;
int n, m, idx;
int find(int u,int k){
if(k<0) return 0;
int l=u, r=u;
for(int i=1;i<=k;i++){
l*=2;
r=r*2+1;
if(l>n) return 0;
}
if(r>n) r=n;
return r-l+1ll;
}
signed main() {
int t;
cin>>t;
while(t--){
int x,k;
cin>>n>>x>>k;
int r=x;
int p=0;
int sum=find(x,k);
while(r/2>=1){
p++;
sum+=find(r/2,k-p)-find(r,k-p-1);
r/=2;
}
cout<<sum<<endl;
}
return 0;
}
F - #(subset sum = K) with Add and Erase
难度: ⭐⭐⭐
题目大意
现在有一个箱子,我们进行q次操作, 操作分两种, 一是往箱子里放一个值为x的球, 二是从箱子里拿出一个值为x的球(题目保证此时箱子里有这个球); 每次操作完都要输出此时用箱子里的球的值凑出k的方式有多少种;
解题思路
因为q和k都很小, 所以可以考虑O(q*k)的复杂度; 因为要计数, 所以可以考虑用dp; 如果往里面放一个值为x的球, 那么就可以让dp[i]+=dp[i-x], 但是要注意, 我们只往里放了一个球, 并且当前转移是把球作用于让i-x变成i, 所以我们此时用的dp[i-x]是每用过新放入的球的状态, 对此我们可以倒序遍历来解决; 对应的, 当我们从里面取出一个值为x的球时, 就是dp[i]-=dp[i-x]; 一样的问题, 该转移意思是这个球之前是让i-x变成i, 那么就要求dp[i-x]这个状态和这个球无关, 但是之前放这个球的时候dp[i-x]已经更新了, 所以我们要在更新dp[i-x]之后更新dp[i], 所以是正序遍历;
神秘代码
#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
#define endl '\n'
using namespace std;
const int N = 2e5 + 10, mod = 998244353;
typedef pair<int, int> PII;
int n, m, idx;
int dp[N];
signed main() {
cin>>n>>m;
dp[0]=1;
while(n--){
char c;
int x;
cin>>c>>x;
if(c=='+'){
for(int i=m;i>=x;i--){
dp[i]=(dp[i]+dp[i-x])%mod;
}
}
else{
for(int i=x;i<=m;i++){
dp[i]=(dp[i]-dp[i-x]+mod)%mod;
}
}
cout<<dp[m]<<endl;
}
return 0;
}