牛客周赛 Round 94
锐评:本场是我第一次全补的一场,难度不大。E题以为必须要连续的1卡了几发;F题很好,苯环哥哥讲的也很好。
A-经典校招题_牛客周赛 Round 94
题目大意:每次可以往上走一级或两级台阶,问从零级台阶走到n级台阶的最少步数
【解题】:每次贪心往上走两级,最后不够两级多走一步就行。其实就是n / 2 上取整。
code:
#include <iostream>using namespace std;typedef long long LL;
const int N = 2e5 + 10;
LL f[N];int main()
{int n; cin >> n;cout << (n + 1) / 2 << endl;return 0;
}
B-小苯购物_牛客周赛 Round 94
题目大意:给定一个商品的价格和三张优惠卷,优惠卷只有在当前物品价格高于a[i]才可使用,使商品价格降低b[i]元,优惠卷可以叠加使用,问怎么样分配可以使得物品价格最小。
【解题】:本题就三张优惠卷,枚举所有的使用顺序才6种,取这6中使用方案的物品价格最小值输出就行。
code:
#include <iostream>using namespace std;int a[4], b[4];int main()
{int t; cin >> t;while(t--){int n; cin >> n;for(int i = 1; i <= 3; i++) cin >> a[i] >> b[i];int minc = 0x3f3f3f3f;int tmp = n;if(a[1] <= n){tmp -= b[1];if(a[2] <= tmp) {tmp -= b[2];if(tmp >= a[3]){tmp -= b[3];}}}minc = min(minc, tmp);tmp = n;if(a[1] <= n){tmp -= b[1];if(a[3] <= tmp) {tmp -= b[3];if(tmp >= a[2]){tmp -= b[2];}}}minc = min(minc, tmp);tmp = n;if(a[2] <= n){tmp -= b[2];if(a[1] <= tmp) {tmp -= b[1];if(tmp >= a[3]){tmp -= b[3];}}}minc = min(minc, tmp);tmp = n;if(a[2] <= n){tmp -= b[2];if(a[3] <= tmp) {tmp -= b[3];if(tmp >= a[1]){tmp -= b[1];}}}minc = min(minc, tmp);tmp = n;if(a[3] <= n){tmp -= b[3];if(a[2] <= tmp) {tmp -= b[2];if(tmp >= a[1]){tmp -= b[1];}}}minc = min(minc, tmp);tmp = n;if(a[3] <= n){tmp -= b[3];if(a[1] <= tmp) {tmp -= b[1];if(tmp >= a[2]){tmp -= b[2];}}}minc = min(minc, tmp);tmp = n;cout << max(minc, 0) << endl;}return 0;
}
C-小苯的与三角形_牛客周赛 Round 94
题目大意:给定一个整数x,构造一个严格小于 x 的正整数 y(1≦y<x),使得x,y,x&y可以构成一个非退化三角形,输出最小的y值。
非退化三角形:满足三条边长均大于 00 且任意两边之和均大于第三边的三角形;
【解题】:本题通过对样例模拟可以发现:对于x的二进制最高位的1,其位置为i, 答案就是pow(2, i - 1),但需要注意如果ans == n,我们是无法构造一个小于x的y的,此时答案就是-1。
其实我们感性理解一下:x > y >= (x and y) ,其实我们只需要保证y + (x and y) > x 就行。
假设x: 001000101110
如果我们刚刚假设的y不是最小,不妨把y再小一点:000111111111
x: 001000101110
y: 0001111111111
x & y: 000000101110
可以发现由于要进位,即使最高位和x一样后面由于累加一定比x最高位后面的数值要小,所有上述的y就是答案。
code:
#include <iostream>
#include <cmath>
using namespace std;void solve()
{int n; cin >> n;int i = 0;for(i = 30; i >= 0; i--){int t = n & (1 << i);if(t) break;}int ans = pow(2, i);if(ans == n) cout << -1 << endl;else cout << ans << endl;
}int main()
{int t; cin >> t;while(t--) solve();return 0;
}
D-小苯的序列染色_牛客周赛 Round 94
题目大意:有一个长度为 n 的字符串 s,一开始所有数字都是 0,下标从 1 开始。
我们可以进行任意次操作:选取一个长度为3的连续段,si,si+1,si+2(1 <= i <= n - 2),将这个长度为3的字段变为110,后面的操作会覆盖前面的。
【解题】:样例模拟:
111001: 000000 -> NO 最后一位是1直接输出NO
11100: 00000 -> 11000 -> 11100 -> YES 如果出现连续大于等于2的连续1子段可以通过覆盖得到。1101010: 0000000 -> 0000110 -> 0011010 -> 1101010 -> YES 对于单独出现的1,我们可以从从后往前操作,然后可以把要求是0但变成1的位置,通过再次选取前面的i,重新踏成0,这样连续的长度为2的1会往前传递,所以对于第一次出现的1,长度大于等于2就可以构造出来。
11110101010100这个样例我们就可以通过先对前面操作,把连续的1操作出来,再从后往前操作,把单独的1操作出来,这样连续长度为2的1就传递到了一开始的连续出现的1了。
code:
#include <iostream>using namespace std;void solve()
{int n; string s; cin >> n >> s;if(s[n - 1] == '1') {cout << "NO" << endl;return;}int cnt = 0;for(int i = 0; i < n; i++){if(s[i] == '1'){for(int j = i; j < n; j++){if(s[j] == '1') cnt++;else break;}if(cnt >= 2) {cout << "YES" << endl;return;}else {cout << "NO" << endl;return;}}}cout << "YES" << endl;
}int main()
{int t; cin >> t;while(t--){solve();}return 0;
}
E-小苯的数字操作_牛客周赛 Round 94
题目大意:给定两个整数n,k。
两种操作:
- 将 n 除以 2 并向下取整;
- 将 n 乘上 2,即将 n 变为 n×2;
可以进行不多于k次上述操作,问最后可以出现 多少中不同的数。
【解题】:我们发现:
对于初始的n,我们直接对它×k次2,就可以出现k + 1个不同的数(包含本身)。
下面我们考虑对其÷2下取整:
下面的n为进行若干次÷2下取整操作后的n,k为当前剩余的操作次数。
假设n为偶数,也就是能除尽,在对其×k次2,除了本身外都出现过了,因此整除的情况答案多1。
假设n为奇数,除不尽的情况,可以发现在操作完后,它的二进制最末尾的1被舍去,然后*2操作,可以产生k + 1(包含本身)的贡献。
code:
#include <iostream>using namespace std;
typedef long long LL;void solve()
{LL n, k; cin >> n >> k;LL ans = 0;bool flag = true;// 因为初始情况也是在下面while循环算的,所以k >= 0而不是1while(n && k >= 0){ if(flag) ans += 1 + k;else ans++;if(n % 2) flag = true;else flag = false;n /= 2;k--;}// 如果出现0while循环结束,最后我们统计上就行if(n == 0) ans++;cout << ans << endl;
}int main()
{int t; cin >> t;while(t--){solve();}return 0;
}
F-小苯的小球分组_牛客周赛 Round 94
题目大意:给定序列a[i],对于所有a[i]的子序列s,求他们的f(s)之和。
其中f(s)的定义为满足下列条件的最少分法:
- 每组最多有 2 个球;
- 组内有 2 个球的组,这 2 个球的颜色不同;
f(s)就是序列的总数n除2上取整和序列元素最大值的最大值。形式化的f(s)= max((n + 1) / 2, maxa);
对于子序列f值要么是前者或者后者,不难发现答案是后者的时候maxa是绝对众数。
所以我们可以把所有的情况先当成(n + 1) / 2处理,然后枚举所有可能的绝对众数,将开始的假设替换成绝对众数就行。
code:
#include <iostream>
#include <unordered_map>
#include <vector>
using namespace std;
#define endl '\n'
typedef long long LL;
const LL MOD = 998244353;
const int N = 5010;
LL f[N], g[N];LL qpow(LL a, LL b, LL MOD)
{LL ret = 1;while(b){if(b & 1) ret = ret * a % MOD;a = a * a % MOD;b >>= 1;}return ret;
}void init()
{int n = 5000;f[0] = 1;for(int i = 1; i <= n; i++) f[i] = f[i - 1] * i % MOD;g[n] = qpow(f[n], MOD - 2, MOD);for(int i = n - 1; i >= 0; i--) {g[i] = g[i + 1] * (i + 1) % MOD;}
}
LL C(int n, int m)
{if(n < m) return 0;return f[n] * g[m] % MOD * g[n - m] % MOD;
}
void solve()
{int tol; cin >> tol;unordered_map<LL, LL> mp;for(int i = 1; i <= tol; i++){int x; cin >> x;mp[x]++;}int n = mp.size();vector<LL> a;for(auto t : mp){a.push_back(t.second);}LL ans = 0;// 全都当成n / 2上取整for(int len = 1; len <= tol; len++){ans += (len + 1) / 2 * C(tol, len) % MOD;ans %= MOD;}// 考虑绝对众数for(int i = 0; i < n; i++){// t为当前颜色的绝对众数for(int t = 1; t <= a[i]; t++){for(int left = 0; left < t; left++){LL ret = t + left; // 当前子序列的总数LL del = (ret + 1) / 2 * C(a[i], t) % MOD * C(tol - a[i], left) % MOD;LL add = t * C(a[i], t) % MOD * C(tol - a[i], left) % MOD;ans += add - del;ans = ((ans % MOD) + MOD) % MOD;}}}cout << ans << endl;
}int main()
{ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);int T = 1;cin >> T;init();
// for(int i = 5000; i <= 5000; i++) cout << g[i] << " ";while(T--){solve();}return 0;
}