传送门
A - 面包店故事
原题
小镇上有一家面包店,面包以 x 元的价格出售,加 y 元可以多加几块培根。小歪带着 n 元来到了面包店,他想知道自己能不能买到加培根的面包?
输入
在一行上输入三个整数 x , y , n x,y,n x,y,n ( 1 ≤ x , y , n ≤ 100 1≤x,y,n≤100 1≤x,y,n≤100) 代表面包的价格、培根的价格和小歪带的钱。
输出
如果小歪能加到培根,在一行上输出 YES ;否则,直接输出 NO 。
测试样例
Input 1
3 1 5
Output 1
YES
Input 2
10 1 10
Output 2
NO
分析
- 小歪要想买到能加培根的面包,那么小歪的钱就必须大于等于面包加培根的钱,即 n ≥ x + y n \ge x + y n≥x+y
#include<bits/stdc++.h>
#define all(a) a.begin(), a.end()
using i32 = int;
using u32 = unsigned int;
using i64 = long long;
using u64 = unsigned long long;
//std::mt19937 rng {std::chrono::steady_clock::now().time_since_epoch().count()};
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
int x, y, n;
std::cin >> x >> y >> n;
if(n >= x + y) {
std::cout << "YES";
} else {
std::cout << "NO";
}
return 0;
}
B - 放课后的故事
原题
小 S 想要举办一个纸飞机大赛,他最新研制出的纸飞机需要 k 张纸才能折成。 为了制作纸飞机,他向班里的 n 个人要了一些纸,第 i 个人提供了 ai 张纸给小 S 研究纸飞机。 放学了,小 S 终于折好了全部的纸飞机,现在有 m 个人留下来和小 S一起飞纸飞机。最多有多少个人能分到纸飞机。
输入
第一行输入三个整数 n , m , k n, m, k n,m,k( 1 ≤ n ≤ 1 0 5 1 \le n \le 10^5 1≤n≤105, 1 ≤ m ≤ 1 0 5 1 \le m \le 10^5 1≤m≤105, 1 ≤ k ≤ 1 0 9 1 \le k \le 10^9 1≤k≤109) 代表班级同学数量、留下来的同学数量和叠一只纸飞机需要的纸的数量。
第二行输入 n n n 个整数 a 1 a_{1} a1, a 2 a_{2} a2,… a n a_{n} an ( 1 ≤ a i ≤ 1 0 9 1 \le a_{i} \le 10^9 1≤ai≤109)代表每一个同学提供的纸的数量
输出
在一行上输出一个整数,代表最多有多少个人能分到纸飞机。
测试样例
Input 1
3 2 5
1 2 4
Output 1
1
Input 2
6 3 4
1 1 4 5 1 4
Output 2
4
分析
- n个同学每个人都给了小S一定数量的纸,而折一个纸飞机需要k张纸,那么可得出小S用这些纸折了多少纸飞机,然后再用这些纸飞机与留下来的同学个数 +1(小S自己) 取最小即可
#include<bits/stdc++.h>
#define all(a) a.begin(), a.end()
using i32 = int;
using u32 = unsigned int;
using i64 = long long;
using u64 = unsigned long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
int n, m, k;
std::cin >> n >> m >> k;
std::vector<int> a(n);
for(int i = 0; i < n; i++) {
std::cin >> a[i];
}
i64 sum = std::accumulate(all(a), 0LL); //计算a数组 + 0 的和
i64 cnt = sum / k;
std::cout << std::min<i64>(cnt, m);
return 0;
}
C - 异或故事
原题
给定 t 组询问,76 每次询问都会给出一个正整数 a ,你需要在区间 [1,10^9] 中找到两个正整数 b 和 c ,使得 b ⊕ c = a 。⊕ 代表按位异或。
输入
每个测试文件均包含多组测试数据。第一行输入一个整数 T T T ( 1 ≤ T ≤ 1 0 5 1 \le T \le 10^5 1≤T≤105) 代表数据组数,每组测试数据描述如下:
在一行上输入一个整数 a a a ( 1 ≤ a ≤ 1 0 9 1 \le a \le 10^9 1≤a≤109) 代表76 给出的初始数字.
输出
对于每一组测试数据,在一行上输出两个正整数,代表你找到的两个值。
如果存在多个解决方案,您可以输出任意一个。
测试样例
Input 1
3
1
5
4
Output 1
2 3
3 6
74 78
分析
-
这一题考察到了位运算,如果对位运算不了解,可以先去学习一下位运算
-
题目给的初始数字最大为 1 0 9 10^9 109,而 int 的最大值是大于这个数的,所以我们可以放心的用int来做题
-
对任一数据 a a a,在二进制下,由一定的 1 1 1 表示,例如 ( 10 ) 10 (10)_{10} (10)10 = ( 1010 ) 2 (1010)_{2} (1010)2 , ( 16 ) 10 (16)_{10} (16)10 = ( 10000 ) 2 (10000)_{2} (10000)2
-
那么对于按位异或运算:
-
如果 a a a 在二进制下 1 1 1 的个数 ≥ 2 \ge 2 ≥2 个,那么我们可以直接取一个 1 1 1 给 b b b,然后把剩余的 1 1 1 给 c c c,那么 一定就有 b ⊕ c = a b ⊕ c = a b⊕c=a
-
如果 a a a 在二进制下 1 1 1 的个数 = 1 =1 =1,,那么我们为了保留这个 1 1 1,同时其他位为 0 0 0 且保证 b , c ∈ [ 1 , 1 0 9 ] b, c \in [1, 10^9] b,c∈[1,109],可以使 b = a + 1 b = a + 1 b=a+1, c = 1 c = 1 c=1
-
注意,这里要特判一下 a = 1 a = 1 a=1的情况,因为 2 ⊕ 1 = 3 2⊕1 = 3 2⊕1=3
#include<bits/stdc++.h>
#define all(a) a.begin(), a.end()
using i32 = int;
using u32 = unsigned int;
using i64 = long long;
using u64 = unsigned long long;
void solve() {
i64 a;
std::cin >> a;
if(a == 1) {
std::cout << 2 << " " << 3 << "\n";
} else {
if(__builtin_popcount(a) == 1) { //统计a在二进制下1的个数
std::cout << 1 << " " << a + 1 << "\n";
} else {
i64 ans = 0;
for(int i = 0; i < 30; i++) {
if((1LL << i) & a) {
ans += 1LL << i;
break;
}
}
std::cout << ans << " " << a - ans << "\n";
}
}
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
i64 t;
std::cin >> t;
while(t--) {
solve();
}
return 0;
}
D - 构造故事
原题
小 S 今天在数学课上学习了三角形,他回家立马拿出了自己的 n 根火柴,想知道从这 n 根火柴中任选 3 根,能否组成一个周长最大的三角形。
由于小 S 只会暴力枚举,所以他把这个问题交给了你,你能帮他解决这个问题吗?
输入
每个测试文件均包含多组测试数据。第一行输入一个整数 T T T ( 1 ≤ T ≤ 20 ) ( 1 \leq T \leq 20) (1≤T≤20)代表数据组数,每组测试数据描述如下:
第一行输入一个整数 n ( 3 ≤ n ≤ 1 0 4 ) n (3 \leq n \leq 10^4) n(3≤n≤104) 代表小S的火柴数量。
第二行输入 n n n 个整数 a 1 , a 2 , . . . a n ( 1 ≤ a i ≤ 1 0 9 ) a_{1}, a_{2},...a_{n} (1 \leq a_{i} \leq 10^9) a1,a2,...an(1≤ai≤109)代表每根火柴的长度
输出
对于每一组测试数据,在一行上输出一个整数,代表能组成周长最大三角形的周长;如果无论如何都无法组成三角形,直接输出 −1 。
测试样例
Input 1
3
6
2 2 10 4 10 6
5
6 1 5 3 3
5
2 2 4 10 6
Output 1
26
14
-1
分析
- 要构造一个三角形,那么就有两边之和 > > > 第三边,对于给定的边,我们可以对其先排序,然后从大开始找(因为要求的是周长最长的三角形),假设三条边按从小到大为 a , b , c a, b, c a,b,c,如果 a , b , c a, b, c a,b,c能构成三角形,那么直接输出 a + b + c a + b + c a+b+c即可,如果不行,那么一定有 a + b < c a + b < c a+b<c,此时我们只能往前找,为什么?因为 a + b a + b a+b 一定是当前最大的两边之和了,如果让另外的边来替换,那么仍然是 < c < c <c的,是不满足题意的,所以只需要往前遍历,如果最终都没能构成三角形,输出 -1即可
#include<bits/stdc++.h>
#define all(a) a.begin(), a.end()
using i32 = int;
using u32 = unsigned int;
using i64 = long long;
using u64 = unsigned long long;
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n + 1);
for(int i = 1; i <= n; i++) {
std::cin >> a[i];
}
std::sort(a.begin() + 1, a.end()); //排序,注意a.begin() + 1
for(int i = n - 2; i >= 0; i--) {
i64 t1 = a[i], t2 = a[i + 1], t3 = a[i + 2]; //三条边
if(t1 + t2 > t3) {
std::cout << t1 + t2 + t3 << "\n";
return;
}
}
std::cout << "-1\n";
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
int t;
std::cin >> t;
while(t--) {
solve();
}
return 0;
}
E - 约会故事
分析
- 首先对输入进行处理,我们可以把一整天的每个时间点看作 24 ∗ 60 24 * 60 24∗60 个点,因此输入就是一个区间修改,可以使用差分来进行处理
- 当输入的第二个时间点小于等于第一个时间点时,说明小C一整天都是开心的,因此设置 s s 1 = 0 ss1 = 0 ss1=0, s s 2 = 24 ∗ 60 ss2 = 24 *60 ss2=24∗60,然后设置 s u m [ s s 1 ] + + , s u m [ s s 2 + 1 ] − − sum[ss1]++, sum[ss2 + 1]-- sum[ss1]++,sum[ss2+1]−−即可完成区间修改,
- 当输入的第二个时间点大于第一个时间点时,说明小C在 s s 1 → s s 2 ss1 \to ss2 ss1→ss2时间内是开心的,同1理设置差分数组sum
- 对于奶茶的名字,我们可以使用C++内置的map来进行管理(具体细节请看代码,很好理解)
- 对于查询,首先要确定发送消息的时间在 00 : 00 → 01 : 59 00:00 \to 01:59 00:00→01:59,即 s s 1 ≤ 120 ss1 \le 120 ss1≤120,或者 s u m [ s s 1 ] = 1 sum[ss1] = 1 sum[ss1]=1(这是一个单点查询),然后再去判断是否比小C提前来到电影院并且是否带对了奶茶,如果这些条件均满足,则可输出 " W i n n e r x q q " "Winner \space xqq" "Winner xqq",否则输出 " J o k e r x q q " "Joker \space xqq" "Joker xqq"
#include<bits/stdc++.h>
#define all(a) a.begin(), a.end()
using i32 = int;
using u32 = unsigned int;
using i64 = long long;
using u64 = unsigned long long;
int sum[24 * 60 + 5];
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
int n, m;
std::cin >> n >> m;
for(int i = 0; i < n; i++) {
std::string s;
std::cin >> s;
int hh = ((s[0] - '0') * 10 + (s[1] - '0'));
int mm = ((s[3] - '0') * 10 + (s[4] - '0'));
int ss1 = hh * 60 + mm;
std::cin >> s;
hh = ((s[0] - '0') * 10 + (s[1] - '0'));
mm = ((s[3] - '0') * 10 + (s[4] - '0'));
int ss2 = hh * 60 + mm;
if(ss1 >= ss2) {
ss1 = 0;
ss2 = 24 * 60;
}
sum[ss1]++;
sum[ss2 + 1]--;
}
for(int i = 1; i <= 24 * 60; i++) {
sum[i] += sum[i - 1];
}
std::map<std::string, int> mp;
for(int i = 0; i < m; i++) {
std::string s;
std::cin >> s;
mp[s] = 1;
}
int q;
std::cin >> q;
for(int i = 0; i < q; i++) {
int ok = 1;
std::string s1, s2, s3, s4;
std::cin >> s1 >> s2 >> s3 >> s4;
int hh1 = ((s1[0] - '0') * 10 + (s1[1] - '0'));
int mm1 = ((s1[3] - '0') * 10 + (s1[4] - '0'));
int ss1 = hh1 * 60 + mm1;
int hh2 = ((s2[0] - '0') * 10 + (s2[1] - '0'));
int mm2 = ((s2[3] - '0') * 10 + (s2[4] - '0'));
int ss2 = hh2 * 60 + mm2;
int hh3 = ((s3[0] - '0') * 10 + (s3[1] - '0'));
int mm3 = ((s3[3] - '0') * 10 + (s3[4] - '0'));
int ss3 = hh3 * 60 + mm3;
if(sum[ss1] == 0) {
ok = 0;
}
if(ss1 >= 120) {
ok = 0;
}
if(!ok) {
std::cout << "Loser xqq\n";
continue;
}
if(ss2 > ss3) {
ok = 0;
}
if(!mp.count(s4)) {
ok = 0;
}
if(ok) {
std::cout << "Winner xqq\n";
} else {
std::cout << "Joker xqq\n";
}
}
return 0;
}
以上。
文章参考的资源,特此鸣谢
1位运算全面总结,关于位运算看这篇就够了
2前缀和与差分 图文并茂 超详细整理(全网最通俗易懂)