A. 经典校招题
有这样一个经典的题目:一共有 n 阶台阶,现在你位于第 0 阶,每次可以向上走 1 阶或 2 阶。
现在,我们想要知道,最少需要走几次才能到达第 n 阶。
解题思路:尽量每次走2, 不得已再走1
#include<bits/stdc++.h>
using namespace std;
int main(){
int n; cin>>n;
if(n&1) cout<<(n-1)/2+1;
else cout<<n/2;
return 0;
}
B. 小苯购物
题目描述
小苯来到了超市,想要购买一件价格为 n 的商品,他有三张优惠券可以使用,具体来说:
其中第 i 张优惠券的最低优惠价格为 ai(即目前的商品价格不低于 ai 元才可以使用),使用后可以减少此商品的价格 bi 元(但最多减到 0 元);
优惠券可以叠加使用,且每张优惠券要么用一次,要么不用。
现在小苯想知道,如果他以最优方式使用这三张优惠券,则最少可以花多少元买走这件商品。输入描述:
每个测试文件均包含多组测试数据。第一行输入一个整数 T(1≦T≦104) 代表数据组数,每组测试数据描述如下:
第一行一个正整数 n(1≦n≦109)。
此后三行,第 i 行两个正整数 ai,bi(1≦ai,bi≦109),描述第 i 张优惠券。
输出描述:
对于每组测试数据,新起一行输出一个整数,表示在使用过优惠券后,此商品的最低价格。
解题思路:只有三张优惠卷, 直接全排列即可, 有顺序的使用这三张优惠卷
#include<bits/stdc++.h>
using namespace std;
using pii=pair<int,int>;
int main(){
int t; cin>>t;
while(t--){
int n; cin>>n;
vector<pii> a(3);
for(int i=0;i<3;i++){
cin>>a[i].first>>a[i].second;
}
int ans=n;
vector<int> b;
for(int i=0;i<3;i++) b.push_back(i);
do{
int p=n;
for(int x:b){
int ai=a[x].first; int bi=a[x].second;
if(p>=ai){
p=max(0,p-bi);
}
}
ans=min(ans,p);
}while(next_permutation(b.begin(),b.end()));
cout<<ans<<endl;
}
return 0;
}
C. 小苯的与三角形
题目描述
小苯给定了一个正整数 x,他对三角形很感兴趣,因此他想要你构造一个严格小于 x 的正整数 y(1≦y<x),使得 x,y,xandyx, 这三个数字作为三角形的边长,可以构成一个非退化的三角形。
但小苯觉得,如果任意构造一个 y,那么问题有些过于简单了,因此他限制你要构造出最小的符合条件的 y。【名词解释】
非退化三角形是指满足三条边长均大于 0 且任意两边之和均大于第三边的三角形。
本题中,and 用于表示按位与运算。如果您需要更多位运算相关的知识,可以参考 OI-Wiki的相关章节 或 百度百科的相关章节。输入描述:
每个测试文件均包含多组测试数据。第一行输入一个整数 T(1≦T≦105) 代表数据组数,每组测试数据描述如下:
在一行上输入一个整数 x(2≦x≦2×109),表示小苯给定的初始数字。
输出描述:
对于每组测试数据,新起一行。如果无解,直接输出一个 −1;否则,输出一个正整数 y(1≦y<x),表示你构造的符合条件的最小 y。
我们可以证明,如果存在解的话,解是唯一的。
解题思路:题目中只给了你一个x, 让你找到最小的y, 满足x, y, x&y 满足三角形的最基本性质,
y<x, 首先x不能是2的幂次, 如果x是2的幂次, 说明它二进制数中只有一个1, eg: 4(100), 8(1000) , 此时x&y=0, 就不满足基本三角形的性质, 如果不是2的幂次, 让y等于最多二进制数最左侧1的大小, 然后x&y=y, 此时恰好满足基本基本三角形的性质
#include <bits/stdc++.h>
using namespace std;
int main() {
int t;
cin>>t;
while(t--) {
int x;
cin>>x;
if((x&(x-1))==0){
cout<<-1<<endl;
}else{
int b=31-__builtin_clz(x);
int y=1<<b;
cout<<y<<endl;
}
}
return 0;
}
D. 小苯的序列染色
题目描述
小苯有一个长度为 n 的字符串 s,一开始所有数字都是 0。下标从 1 开始。
现在,他可以使用一个字符串 t="110",来对 s 进行若干次「染色」操作,具体的,小苯可以做以下操作任意次:
从 s 中选择一个长度恰好为 3 的连续段 si,si+1,si+2(1≦i≦n−2),并将这三个字符依次赋值为: ‘1’,‘1’,‘0’;
换句话说,使用 ttt 对 sss 中长度为 333 的连续段进行覆盖,被覆盖的部分操作后会变成 “110"。注意:后来的操作会覆盖先前的操作。
现在小苯想知道,如果给定了一个最终的、仅由字符‘0’ 和 ‘1’ 构成的期望结果字符串 s,他想知道 s 能否通过任意次(可以为 0 次)「染色」操作变成 s′。请你帮帮他吧。输入描述:
每个测试文件均包含多组测试数据。第一行输入一个整数 T(1≦T≦100)代表数据组数,每组测试数据描述如下:
第一行输入一个整数 n(1≦n≦2×105),表示序列 s 的长度。
第二行输入一个长度为 n,仅由字符 ‘0’ 和 ‘1’ 构成的字符串 s′,表示小苯期望的最终结果。
除此之外,保证单个测试文件的 n 之和不超过2×10^5。输出描述:
对于每组测试数据,新起一行。如果存在一种「染色」方案使得 s 可以变成 s′,则输出 YES,否则输出 NO。
解题思路:其实这题很简单,但是我赛时写了很长时间, 代码也写的很长, 你自己多画就会发现, 要想输出YES, 就要满足这两个性质;1. (首次出现)前两个1必须是连续的 2. s的最后一位必须是0
正确代码:
#include<bits/stdc++.h>
using namespace std;
int main(){
int t; cin>>t;
while(t--){
int n; cin>>n;
string s; cin>>s;
vector<int> p;
//1. 先统计1的位置
for(int i=0;i<n;i++){
if(s[i]=='1'){
p.push_back(i);
}
}
if(p.empty()) { cout<<"YES"<<endl; continue; }
if(n<=2){ cout<<"NO"<<endl; continue; }
else{
if(p.size()==1||s.back()=='1'){
cout<<"NO"<<endl;
}else if(p[1]==p[0]+1){
cout<<"YES"<<endl;
}else{
cout<<"NO"<<endl;
}
}
p.clear();
}
return 0;
}
赛时代码:
#include <bits/stdc++.h>
using namespace std;
int main() {
int t;
cin >> t;
string m = "110";
while (t--) {
int n;
cin >> n;
string s;
cin >> s;
if (n < 3) {
bool a= true;
for (char c : s) {
if (c == '1') {
a = false;
break;
}
}
cout<<(a?"YES\n":"NO\n");
continue;
}
vector<int> b(n, 0);
vector<char> cur(n);
for (int i = 0; i < n; i++) {
cur[i] = s[i];
}
for (int i = 0; i + 2 < n; i++) {
int cnt = 0;
for (int j = 0; j < 3; j++) {
if (cur[i + j] !=m[j]) {
cnt++;
}
}
b[i] = cnt;
}
queue<int> q;
vector<bool> c(n, false);
for (int i = 0; i + 2 < n; i++) {
if (b[i] == 0) {
q.push(i);
c[i] = true;
}
}
while (!q.empty()) {
int i = q.front();
q.pop();
bool f = false;
for (int j = 0; j < 3; j++) {
if (cur[i + j] != '#') {
f = true;
break;
}
}
if (!f) continue;
for (int j = 0; j < 3; j++) {
int p = i + j;
if (cur[p] == '#') continue;
char o_c=cur[p];
cur[p] = '#';
for (int k = p - 2; k <= p; k++) {
if (k < 0 || k + 2 >= n) continue;
int j2 = p - k;
if (o_c != m[j2]) {
b[k]--;
if (b[k]==0&&!b[k]) {
q.push(k);
c[k] = true;
}
}
}
}
}
bool f=true;
for (int i = 0; i < n; i++) {
if (cur[i] == '1') {
f = false;
break;
}
}
cout<<(f?"YES\n":"NO\n");
}
return 0;
}
E. 小苯的数字操作
题目描述
小苯有一个正整数 n,他可以对 n 做不超过 k 轮操作,每轮操作从以下两种中任选一种进行:
∙将 n 除以 2 并向下取整,即将 n 变为 ⌊n2⌋;
,∙将 n 乘上 2,即将 n 变为 n×2。
他想知道,如果他重复取出一个新的 n 进行操作,每一次操作轮数不超过 k 轮(可以为 0 轮),那么操作过程中总共会产生多少个不同的数字,请你帮他数一数吧。输入描述:
每个测试文件均包含多组测试数据。第一行输入一个整数 T(1≦T≦104) 代表数据组数,每组测试数据描述如下:
在单独的一行输入两个空格分割的正整数 n,k(1≦n,k≦109),表示小苯拥有的数字、以及他最多对 n 进行操作的轮数。
输出描述:
对于每组测试数据,新起一行。输出一个整数,表示操作过程中总共会产生多少个不同的数字。
解题思路:常规dfs会超时, 使用使用一个哈希表 a来记录,其中第一个元素 b 表示从 n 到 v 需要除以2的次数,第二个元素 c 表示 v 的二进制表示中最低位的0的个数(即 __builtin_ctzll(v),也就是 v 的二进制中末尾的连续0的个数)
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main(){
int t;
cin>>t;
while(t--) {
int n, k;
cin>>n>>k;
unordered_map<ll, pair<ll,ll>> a;
ll cur=n;
ll d=0;
while(cur>0){
ll z= __builtin_ctzll(cur);
ll v=cur>>z;
if (!a.count(v)) {
a[v]={d, z};
}
d++;
cur>>=1;
}
ll d_=d;
ll ans=0;
for (auto&x : a) {
ll v=x.first,b=x.second.first,c=x.second.second;
if(b>k) continue;
ll r=k-b,s=c-r;
if(s<0) s=0;
ll e=c+r;
ans+=(e-s+1);
}
if(k>=d_) {
ans+=1;
}
cout<<ans<<endl;
}
return 0;
}
F. 小苯的小球分组
题目描述
小苯有 n个小球,其中第 i 个小球的颜色为 ai。从其中取出一些小球,组成一个小球集合 S。定义小球集合 f(S),表示将小球集合 S 分为若干组,满足以下所有条件的最少分组个数:
每组最多有 2 个球;
组内有 2 个球的组,这 2 个球的颜色不同。
现在小苯希望你求出这 n 个小球的所有非空子集(按位置区分)所对应的 f 函数值之和。由于答案可能很大,请将答案对 998 244 353取模后输出。输入描述:
每个测试文件均包含多组测试数据。第一行输入一个整数 T(1≦T≦100)代表数据组数,每组测试数据描述如下:
第一行输入一个整数 n(1≦n≦5000),表示小球的总个数。
第二行输入 n 个整数 a1,a2,…,an(1≦ai≦109),表示每个小球的颜色。除此之外,保证单个测试文件的 n 之和不超过 5×10^3
输出描述:
对于每一组测试数据,新起一行。输出一个整数,表示所有子序列的 f 函数值之和。由于结果可能很大,请将答案对 998 244 353 取模后输出。
视频讲解: F_哔哩哔哩_bilibili
解题思路:我想明白再给你们讲吧,不是很懂....
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5010;
const int mod = 998244353;
int a[N];
int fact[N], infact[N];
int qmi(int a,int b){
int r = 1;
while(b){
if(b & 1) r = r * a % mod;
a = a * a % mod;
b >>= 1;
}
return r;
}
int C(int n,int k){
if(k < 0 || k > n) return 0;
return fact[n] * infact[k] % mod * infact[n-k] % mod;
}
void solve(){
int n;
cin >> n;
map<int,int> mp;
for(int i = 1; i <= n; i++){
cin >> a[i];
mp[a[i]]++;
}
vector<int> vec;
vec.reserve(mp.size());
for(auto &kv: mp) vec.push_back(kv.second);
sort(vec.begin(), vec.end(), greater<int>());
int ans = 0;
for(int i = 1; i <= n; i++){
int t = (i + 1) / 2;
int res = 0;
for(int j = t+1; j <= i; j++){
for(int cnt: vec){
if(cnt < j) break;
int ways = C(cnt, j) * C(n - cnt, i - j) % mod;
res = (res + ways) % mod;
ans = (ans + ways * j) % mod;
}
}
int rest = (C(n,i) - res + mod) % mod;
ans = (ans + rest * t) % mod;
}
cout << ans << "\n";
}
signed main(){
fact[0] = infact[0] = 1;
for(int i = 1; i < N; i++){
fact[i] = fact[i-1] * i % mod;
infact[i] = qmi(fact[i], mod-2);
}
int T;
cin >> T;
while(T--) solve();
return 0;
}
感谢大家的点赞和关注,你们的支持是我创作的动力!
注:我是真不擅长思维题(其实什么也不擅长, 哈哈哈)