1. 基础贪心与枚举
1.1 基础贪心
1.1.1 小紫的劣势博弈
问题与解决方法
-
sort中普通数组用
sort(a+1,a+n+1) -
从大到小 : 加
greater<int>()
思路:
- 先排序
- 然后分别加减
代码:
cpp
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n;
int a[N];
int x;
int main()
{
cin >> n;
for(int i = 1 ; i <= n ; i++) cin >> a[i];
sort(a+1 , a+n+1);
for(int i = 1 ; i <= n ; i++)
{
if(i % 2 == 1) x += a[i];
if(i % 2 == 0) x -= a[i];
}
cout << x;
return 0;
}
1.1.2 小梦的AB交换
!洛谷
U535982 C-小梦的AB交换
题目描述
小梦有一个长度为 2 ∗ n 2 * n 2∗n 的 A B AB AB 串 s s s,即 s s s 中只包含 " A A A" 和 " B B B" 两种字符,且其中恰好有 n n n 个 " A A A" 和 n n n 个 " B B B"。
他可以对 s s s 执行以下操作:
∙ \bullet ∙ 选择 i , j ( 1 ≤ i , j ≤ 2 ⋅ n , i ≠ j ) i, j\ (1 \leq i, j \leq 2\cdot n, i \ne j) i,j (1≤i,j≤2⋅n,i=j),并交换 s i s_i si 和 s j s_j sj。
他想知道,需要至少多少次操作,才能使得 s s s 满足相邻的字符不相同,请你帮他算一算吧。
输入格式
本题有多组测试数据。
输入的第一行包含一个正整数 T T T,表示数据组数。
接下来包含 T T T 组数据,每组数据的格式如下:
第一行一个正整数 n n n,表示 s s s 长度的一半。
第二行一个长度为 2 ∗ n 2*n 2∗n 的字符串 s s s,保证只由 " A A A", " B B B" 两种字符构成。
输出格式
对于每组测试数据:
在单独的一行输出一个整数,表示最少进行的操作次数。
输入输出样例 #1
输入 #1
2 3 AAABBB 3 ABAABB输出 #1
1 1说明/提示
【样例 1 解释】
交换 s 2 = A s_2 = A s2=A 和 s 5 = B s_5=B s5=B,得到 s = s= s= " A B A B A B \rm ABABAB ABABAB",满足题意,一次交换即可。
【数据范围】
令 N N N 表示 T T T 组数据中 n n n 的总和。
对于 50 % \rm 50\% 50% 的数据有: T = 1 , 1 ≤ N ≤ 3 T = 1, 1 \leq N\leq 3 T=1,1≤N≤3。
对于所有的测试数据有: 1 ≤ T ≤ 100 , 1 ≤ N ≤ 10 6 1 \leq T \leq 100, 1 \leq N \leq 10^6 1≤T≤100,1≤N≤106。
问题与解决方法
- 多思考
- 注意细节,要考虑到所有情况
- 多用几个不同的样例测试
思路:
- 两个一组,分为n组
- 每组只有
"AA" "BB" "AB" " BA"四种情况 - 每种情况有几个, 记下
"AA"一定等于"BB"数(因为'A'与'B'个数相同)每个"AA"与"BB"交换一次即可达到目标"AB"与"BA"只有一个时, 不用交换,有多个时,交换""AB"与"BA"的个数最少的个数" 即可
代码:
cpp
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int n , T;
int a[N];
int main()
{
cin >> T;
while(T--)
{
string s;
cin >> n >> s;
int x = 0;
//AA 1 BB 2 AB 3 BA 4
//存以上四种类型的个数
for(int i = 0; i < 2*n; i+=2)
{
if(s[i] == 'A')
{
if(s[i+1] == s[i])a[1]++;
else a[3]++;
}
else
{
if(s[i+1] == s[i])a[2]++;
else a[4]++;
}
}
//处理 AA BB
if(a[1] == a[2])
{
x+=a[1];
a[1] = 0;
a[2] = 0;
}
//处理 AB BA
if(a[3] == a[4])
{
x+=a[3];
a[3] = 0;
a[4] = 0;
}
else
{
if(max(a[3], a[4]) != 1) x += min(a[3], a[4]);
a[3] = 0;
a[4] = 0;
}
cout << x << endl;
}
return 0;
}
1.1.3 小苯的Z串匹配

问题与解决方法
- 一定要注意长度限制, 尽量多用longlong, 或者涉及×+改为/-运算
思路:
- 当
s[i]为>时,a[i]必须是正数 - 当
s[i]为<时,a[i]必须是负数 - 当
s[i]为=时,a[i]和a[i-1]必须同号
代码:
cpp
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
typedef long long LL;
LL n, T;
LL a[N];
LL x;
int main()
{
cin >> T;
while(T--)
{
x = 0;
string s;
cin >> n;
for(int i = 0; i < n; i++) cin >> a[i];
cin >> s;
for(int i = 0; i < n; i++)
{
if(s[i] == '>')
{
if(a[i] <= 0)
{
x++;
a[i] = 1;
}
}
else if(s[i] == '<')
{
if(a[i] >= 0)
{
x++;
a[i] = -1;
}
}
else
{
if(a[i] * a[i-1] <= 0)
{
x++;
if(a[i-1] > 0) a[i] = 1;
else a[i] = -1;
}
}
}
cout << x << endl;
}
return 0;
}
1.2 基础枚举
1.2.1 好数
!洛谷
P10424 [蓝桥杯 2024 省 B] 好数
题目描述
一个整数如果按从低位到高位的顺序,奇数位(个位、百位、万位......)上的数字是奇数,偶数位(十位、千位、十万位......)上的数字是偶数,我们就称之为"好数"。
给定一个正整数 N N N,请计算从 1 1 1 到 N N N 一共有多少个好数。
输入格式
一个整数 N N N。
输出格式
一个整数代表答案。
输入输出样例 #1
输入 #1
24输出 #1
7输入输出样例 #2
输入 #2
2024输出 #2
150说明/提示
样例 1 解释
24 24 24 以内的好数有 1 , 3 , 5 , 7 , 9 , 21 , 23 1,3,5,7,9,21,23 1,3,5,7,9,21,23,一共 7 7 7 个。
数据规模与约定
- 对于 10 % 10\% 10% 的测试数据, 1 ≤ N ≤ 100 1 \leq N \le 100 1≤N≤100。
- 对于全部的测试数据, 1 ≤ N ≤ 10 7 1 \le N \leq 10^7 1≤N≤107。
问题与解决方法
思路:
- 暴力枚举
代码:
cpp
#include<bits/stdc++.h>
using namespace std;
int N;
int ret;
bool isGood(int n)
{
int k = 0;
while(n)
{
k++;
if(k%2 == 1 && n%2 == 0)return false;
if(k%2 == 0 && n%2 == 1)return false;
n/=10;
}
return true;
}
int main()
{
cin >> N;
for(int i = 1; i <= N ; i++)
{
if(isGood(i)) ret++;
}
cout << ret;
return 0;
}
1.2.2 小红杀怪
问题与解决方法
- 输出 0-10的随机数
cpp
cout << rand()%10 << endl;
- 向上取整 (a/x)
cpp
(a + x - 1) / x
思路:
- 暴力枚举所有情况
代码:
- 贪心思路 但只过了80%
cpp
#include<bits/stdc++.h>
using namespace std;
int a, b, x, y, aa, bb;
int num, num2, num3;
int main()
{
cin >> a >> b >> x >> y;
if(x>y)
{
num += a/x;
a = a%x;
num += b/x;
b = b%x;
aa = a;
bb = b;
num2 = 0;
while(aa>0)
{
aa -= x;
num2++;
}
while(bb>0)
{
bb -= x;
num2++;
}
num3 = 0;
while(a>0 || b>0)
{
a -= y;
b -= y;
num3++;
}
num += min(num2, num3);
}
else
{
num += min(a,b)/y;
a = a%y;
b = b%y;
aa = a;
bb = b;
num2 = 0;
while(aa>0)
{
aa -= x;
num2++;
}
while(bb>0)
{
bb -= x;
num2++;
}
num3 = 0;
while(a>0 || b>0)
{
a -= y;
b -= y;
num3++;
}
num += min(num2, num3);
}
cout << num;
return 0;
}
- 枚举
cpp
#include<bits/stdc++.h>
using namespace std;
int a, b, x, y, aa, bb;
int num;
int ret = 1e8;
int main()
{
cin >> a >> b >> x >> y;
for(int i = 0 ; i <= (max(a,b)+y-1)/y; i++)
{
//初始化
num = 0;
aa = a;
bb = b;
//群伤
num += i;
aa -= i*y;
bb -= i*y;
//单伤
num += (aa>0) ? (aa + x - 1) / x : 0;
num += (bb>0) ? (bb + x - 1) / x : 0;
//记录最小值
ret = min(ret,num);
}
cout << ret;
return 0;
}
1.2.3 训练士兵
!洛谷
P10387 [蓝桥杯 2024 省 A] 训练士兵
P10387 [蓝桥杯 2024 省 A] 训练士兵 - 洛谷
题目描述
在蓝桥王国中,有 n n n 名士兵,这些士兵需要接受一系列特殊的训练,以提升他们的战斗技能。对于第 i i i 名士兵来说,进行一次训练所需的成本为 p i p_i pi 枚金币,而要想成为顶尖战士,他至少需要进行 c i c_i ci 次训练。
为了确保训练的高效性,王国推出了一种组团训练的方案。该方案包含每位士兵所需的一次训练,且总共只需支付 S S S 枚金币(组团训练方案可以多次购买,即士兵可以进行多次组团训练)。
作为训练指挥官,请你计算出最少需要花费多少金币,才能使得所有的士兵都成为顶尖战士?
输入格式
输入的第一行包含两个整数 n n n 和 S ,用一个空格分隔,表示士兵的数量和进行一次组团训练所需的金币数。
接下来的 n n n 行,每行包含两个整数 p i p_i pi 和 c i c_i ci,用一个空格分隔,表示第 i i i 名士兵进行一次训练的金币成本和要成为顶尖战士所需的训练次数。
输出格式
输出一行包含一个整数,表示使所有士兵成为顶尖战士所需的最少金币数。
输入输出样例 #1
输入 #1
3 6 5 2 2 4 3 2输出 #1
16说明/提示
花费金币最少的训练方式为:进行 2 2 2 次组团训练,花费 2 × 6 = 12 2 × 6 = 12 2×6=12 枚金币,此时士兵 1 , 3 1, 3 1,3 已成为顶尖战士;再花费 4 4 4 枚金币,让士兵 2 2 2 进行两次训练,成为顶尖战士。总花费为 12 + 4 = 16 12 + 4 = 16 12+4=16。
对于 40 % 40\% 40% 的评测用例, 1 ≤ n ≤ 10 3 , 1 ≤ p i , c i ≤ 10 5 , 1 ≤ S ≤ 10 7 1 ≤ n ≤ 10^3,1 ≤ p_i , c_i ≤ 10^5,1 ≤ S ≤ 10^7 1≤n≤103,1≤pi,ci≤105,1≤S≤107。
对于所有评测用例, 1 ≤ n ≤ 10 5 , 1 ≤ p i , c i ≤ 10 6 , 1 ≤ S ≤ 10 10 1 ≤ n ≤ 10^5,1 ≤ p_i , c_i ≤ 10^6,1 ≤ S ≤ 10^{10} 1≤n≤105,1≤pi,ci≤106,1≤S≤1010。
问题与解决方法
- 注意训练次数不能为负
cpp
num += p[j] * ((c[j] - i)>0 ? (c[j] - i) : 0);
思路:
代码:
- 枚举做法 会超时
cpp
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
long long p[N], c[N];
long long n, S;
long long maxc = -1;
long long num, ret = 1e17;
int main()
{
scanf("%lld %lld", &n , &S);
for(int i = 1; i<= n; i++)
{
scanf("%lld %lld", &p[i] , &c[i]);
maxc = max(maxc, c[i]);
}
for(int i = 0; i<= maxc; i++)
{
//初始化
num = 0;
//团练
num += i*S;
//单练
for(int j = 1; j <= n; j++)
{
num += p[j] * ((c[j] - i)>0 ? (c[j] - i) : 0);
}
ret = min(ret, num);
}
printf("%lld", ret);
return 0;
}
- 贪心
cpp
2. 前缀和
小红的陡峭值(三)(hard)
D-小红的陡峭值(三)(hard)_牛客周赛 Round 84

思路:
- 前缀和
代码:
cpp
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
typedef long long LL;
LL n, k;
string s;
LL a[N];
LL sum;
int main()
{
//优化输入输出
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
//输入数据
cin >> n >> k;
cin >> s;
//构造前缀和数组
for(int i = 1; i < s.size(); i++) a[i] = abs(s[i] - s[i-1]) + a[i-1];
//加子段和
for(int i = k-1; i < n; i++) sum += a[i] - a[i-k+1];
//输出
cout << sum;
return 0;
}
3. 二进制
小苯的异或疑惑(hard)
M-小苯的异或疑惑(hard)_北京信息科技大学第十五届程序设计竞赛(同步赛)

思路:
异或运算有一个性质:一个数异或偶数次等于 0,异或奇数次等于它本身。
我们需要分析在最终的答案 ans = f(1,2) ^ f(1,3) ^ ... ^ f(n-1,n) 中,原数组中的每一个元素 a i a_i ai 到底被异或了多少次。
-
分析贡献次数 :
对于数组中的某一个元素 a i a_i ai,它会在哪些 f ( i , j ) f(i, j) f(i,j) 中出现?
- 当 i i i 作为第一个下标时: f ( i , i + 1 ) , f ( i , i + 2 ) , ... , f ( i , n ) f(i, i+1), f(i, i+2), \dots, f(i, n) f(i,i+1),f(i,i+2),...,f(i,n)。这种情况共有 ( n − i ) (n-i) (n−i) 次。
- 当 i i i 作为第二个下标时: f ( 1 , i ) , f ( 2 , i ) , ... , f ( i − 1 , i ) f(1, i), f(2, i), \dots, f(i-1, i) f(1,i),f(2,i),...,f(i−1,i)。这种情况共有 ( i − 1 ) (i-1) (i−1) 次。
所以, a i a_i ai 总共出现的次数为: ( n − i ) + ( i − 1 ) = n − 1 (n-i) + (i-1) = n-1 (n−i)+(i−1)=n−1 次。
-
根据奇偶性判断:
- 如果 n − 1 n-1 n−1 是偶数 ,那么 a i a_i ai 在最终结果中被异或了偶数次,结果为 0,对答案没有贡献。
- 如果 n − 1 n-1 n−1 是奇数 ,那么 a i a_i ai 在最终结果中被异或了奇数次,相当于 a i a_i ai 本身,对答案的贡献就是 a i a_i ai。
-
结论:
- 当 n n n 为奇数 时, n − 1 n-1 n−1 是偶数。所有数的贡献都抵消了,答案为 0。
- 当 n n n 为偶数 时, n − 1 n-1 n−1 是奇数。所有数都会贡献一次,答案为 所有 a i a_i ai 的异或和。
代码:
- 暴力 会超时
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
LL n;
LL ret;
LL a[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
for(int i = 1; i < n; i++)
{
for(int j = i+1; j <= n; j++)
{
ret = ret^a[i]^a[j];
}
}
cout << ret;
return 0;
}
- 正确做法
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
LL n;
LL ret;
LL a[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n;
// 如果 n 是奇数,每个数出现 n-1 (偶数) 次,异或结果为 0
// 如果 n 是偶数,每个数出现 n-1 (奇数) 次,异或结果为所有数的异或和
if (n % 2 == 0)
{
for(int i = 1; i <= n; i++)
{
cin >> a[i];
ret ^= a[i];
}
}
else
{
for(int i = 1; i <= n; i++)
{
cin >> a[i];
}
ret = 0;
}
cout << ret;
return 0;
}
4. 洛谷题单
4.1 A-B 数对
!洛谷
P1102 A-B 数对
题目背景
出题是一件痛苦的事情!
相同的题目看多了也会有审美疲劳,于是我舍弃了大家所熟悉的 A+B Problem,改用 A-B 了哈哈!
题目描述
给出一串正整数数列以及一个正整数 C C C,要求计算出所有满足 A − B = C A - B = C A−B=C 的数对的个数(不同位置的数字一样的数对算不同的数对)。
输入格式
输入共两行。
第一行,两个正整数 N , C N,C N,C。
第二行, N N N 个正整数,作为要求处理的那串数。
输出格式
一行,表示该串正整数中包含的满足 A − B = C A - B = C A−B=C 的数对的个数。
输入输出样例 #1
输入 #1
4 1 1 1 2 3输出 #1
3说明/提示
对于 75 % 75\% 75% 的数据, 1 ≤ N ≤ 2000 1 \leq N \leq 2000 1≤N≤2000。
对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 2 × 10 5 1 \leq N \leq 2 \times 10^5 1≤N≤2×105, 0 ≤ a i < 2 30 0 \leq a_i <2^{30} 0≤ai<230, 1 ≤ C < 2 30 1 \leq C < 2^{30} 1≤C<230。
2017/4/29 新添数据两组
思路:
代码:
- 暴力 -> 3个测试点超时
cpp
#include<bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
long long n, c;
cin >> n >> c;
long long a[n];
long long ret = 0;
for(long long i = 0; i < n; i++) cin >> a[i];
sort(a,a+n,greater<int>());
// for(long long i = 0; i < n; i++) cout << a[i] << " " ;
// cout << endl;
for(long long i = 0; i < n-1; i++)
{
for(long long j = i+1; j < n; j++)
{
if(a[i]-a[j] == c) ret++;
// cout << endl << "a[i]=" << a[i] << endl << "a[j]=" << a[j] << endl;
// cout << a[i]-a[j] << endl;
}
}
cout << ret;
return 0;
}
- 用map
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
LL n, c;
cin >> n >> c;
map<LL,LL> m;
vector<LL>a(n,0);
for(LL i = 0 ; i < n; i++)
{
cin >> a[i];
m[a[i]]++;
}
LL ret = 0;
for(LL i = 0; i < n; i++)
{
LL t = a[i] + c;
if(m.find(t) != m.end())
{
ret += m[t];
}
}
cout << ret;
return 0;
}
!洛谷
4.2 保龄球
P1918 保龄球
题目描述
DL 算缘分算得很烦闷,所以常常到体育馆去打保龄球解闷。因为他保龄球已经打了几十年了,所以技术上不成问题,于是他就想玩点新花招。
DL 的视力真的很不错,竟然能够数清楚在他前方十米左右每个位置的瓶子的数量。他突然发现这是一个炫耀自己好视力的借口------他看清远方瓶子的个数后从某个位置发球,这样就能打倒一定数量的瓶子。
◯ ◯ ◯ \bigcirc \bigcirc \bigcirc ◯◯◯
◯ ◯ ◯ ◯ \bigcirc \bigcirc \bigcirc\ \bigcirc ◯◯◯ ◯
◯ \bigcirc ◯
◯ ◯ \bigcirc\ \bigcirc ◯ ◯
如上图,每个 " ◯ \bigcirc ◯" 代表一个瓶子。如果 DL 想要打倒 3 3 3 个瓶子就在 1 1 1 位置发球,想要打倒 4 4 4 个瓶子就在 2 2 2 位置发球。
现在他想要打倒 m m m 个瓶子。他告诉你每个位置的瓶子数,请你给他一个发球位置。
输入格式
第一行包含一个正整数 n n n,表示位置数。
第二行包含 n n n 个正整数 a i a_i ai ,表示第 i i i 个位置的瓶子数,保证各个位置的瓶子数不同。
第三行包含一个正整数 Q Q Q,表示 DL 发球的次数。
第四行至文件末尾,每行包含一个正整数 m m m,表示 DL 需要打倒 m m m 个瓶子。
输出格式
共 Q Q Q 行。每行包含一个整数,第 i i i 行的整数表示 DL 第 i i i 次的发球位置。若无解,则输出 0 0 0。
输入输出样例 #1
输入 #1
5 1 2 4 3 5 2 4 7输出 #1
3 0说明/提示
【数据范围】
对于 50 % 50\% 50% 的数据, 1 ≤ n , Q ≤ 1000 , 1 ≤ a i , m ≤ 10 5 1 \leq n, Q \leq 1000, 1 \leq a_i, m \leq 10^5 1≤n,Q≤1000,1≤ai,m≤105。
对于 100 % 100\% 100% 的数据, 1 ≤ n , Q ≤ 100000 , 1 ≤ a i , m ≤ 10 9 1 \leq n,Q \leq 100000, 1 \leq a_i, m \leq 10^9 1≤n,Q≤100000,1≤ai,m≤109。
问题
- map的find找的是第一个元素
- ->first -> second
代码:
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
ll n;
cin >> n;
map<ll,ll> mm;
ll a;
for(int i = 0; i < n; i++)
{
cin >> a;
mm[a] = i;
}
ll q, m;
cin >> q;
while(q--)
{
cin >> m;
if(mm.find(m) != mm.end())
{
auto it = mm.find(m);
cout << (it -> second) + 1 << endl;
}
else
{
cout << "0" << endl;
}
}
return 0;
}
4.3 学籍管理
!洛谷
P5266 【深基17.例6】学籍管理
题目描述
您要设计一个学籍管理系统,最开始学籍数据是空的,然后该系统能够支持下面的操作(不超过 10 5 10^5 105 条):
- 插入与修改,格式
1 NAME SCORE:在系统中插入姓名为 NAME \texttt{NAME} NAME(由字母和数字组成不超过 20 20 20 个字符的字符串,区分大小写),分数为 SCORE \texttt{SCORE} SCORE( 0 < SCORE < 2 31 0<\texttt{SCORE}<2^{31} 0<SCORE<231) 的学生。如果已经有同名的学生则更新这名学生的成绩为 SCORE \texttt{SCORE} SCORE。如果成功插入或者修改则输出OK。- 查询,格式
2 NAME:在系统中查询姓名为 NAME \texttt{NAME} NAME 的学生的成绩。如果没能找到这名学生则输出Not found,否则输出该生成绩。- 删除,格式
3 NAME:在系统中删除姓名为 NAME \texttt{NAME} NAME 的学生信息。如果没能找到这名学生则输出Not found,否则输出Deleted successfully。- 汇总,格式
4:输出系统中学生数量。输入格式
第一行,输入一个正整数 Q Q Q( 1 ≤ Q ≤ 10 5 1 \le Q \le 10^5 1≤Q≤105),表示操作数量。
接下来 Q Q Q 行,每行先输入一个正整数 o p op op( o p ∈ [ 1 , 4 ] op \in [1,4] op∈[1,4]),表示操作种类。接着:
- 如果 o p = 1 op = 1 op=1,则再输入一个字符串 NAME \texttt{NAME} NAME 以及一个正整数 SCORE \texttt{SCORE} SCORE,含义见题目描述。
- 如果 o p = 2 op = 2 op=2,则再输入一个字符串 NAME \texttt{NAME} NAME,含义见题目描述。
- 如果 o p = 3 op = 3 op=3,则再输入一个字符串 NAME \texttt{NAME} NAME,含义见题目描述。
- 如果 o p = 4 op = 4 op=4,则无需再输入其他内容。
输出格式
共输出 Q Q Q 行,每行输出一个字符串或正整数,为对应操作的处理结果,具体含义见题目描述。
输入输出样例 #1
输入 #1
5 1 lxl 10 2 lxl 3 lxl 2 lxl 4输出 #1
OK 10 Deleted successfully Not found 0
- map应用
代码:
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll q, op;
map<string,ll> m;
string s;
ll score;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> q;
while(q--)
{
cin >> op;
switch(op)
{
case 1:
cin >> s >> score;
m[s] = score;
cout << "OK" << "\n";
break;
case 2:
cin >> s;
if(m.find(s) != m.end())
{
cout << m[s] << "\n";
}
else
{
cout << "Not found" << "\n";
}
break;
case 3:
cin >> s;
if(m.find(s) != m.end())
{
m.erase(s);
cout << "Deleted successfully" << "\n";
}
else
{
cout << "Not found" << "\n";
}
break;
case 4:
cout << m.size() << endl;
break;
}
}
return 0;
}
4.4 木材仓库
!洛谷
P5250 【深基17.例5】木材仓库
题目描述
博艾市有一个木材仓库,里面可以存储各种长度的木材,但是保证没有两个木材的长度是相同的。作为仓库负责人,你有时候会进货,有时候会出货,因此需要维护这个库存。有不超过 10 5 10^5 105 条的操作:
- 进货,格式
1 Length:在仓库中放入一根长度为 L e n g t h Length Length(不超过 10 9 10^9 109)的木材。如果已经有相同长度的木材那么输出Already Exist。- 出货,格式
2 Length:从仓库中取出长度为 L e n g t h Length Length 的木材。如果没有刚好长度的木材,取出仓库中存在的和要求长度最接近的木材。如果有多根木材符合要求,取出比较短的一根。输出取出的木材长度。如果仓库是空的,输出Empty。输入格式
第一行一个数 m m m 代表操作次数。
接下来 m m m 行,每行一次操作,格式如题目描述所示。
输出格式
对于每次操作,按照题目描述要求输出答案。
输入输出样例 #1
输入 #1
7 1 1 1 5 1 3 2 3 2 3 2 3 2 3输出 #1
3 1 5 Empty
问题
- m->first 要先赋值给其他,再运算
- lower_bound与upper_bound的使用
- 所有情况都要考虑到
代码:
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n, op;
map<ll,ll> m;
ll l;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n;
while(n--)
{
cin >> op;
switch(op)
{
case 1:
cin >> l;
if(m.find(l) != m.end())
{
cout << "Already Exist" << "\n";
}
else
{
m[l]++;
}
break;
case 2:
cin >> l;
if(m.empty())
{
cout << "Empty" << "\n";
break;
}
auto it = m.lower_bound(l); // 第一个 >= l 的
// 情况1:l 比所有元素都大 → it == m.end()
if(it == m.end())
{
auto it3 = prev(m.end()); // 最后一个元素(最大)
cout << it3->first << "\n";
m.erase(it3->first);
break;
}
// 情况2:存在等于 l 的
if(it->first == l)
{
cout << l << "\n";
m.erase(l);
break;
}
// 情况3:l 比所有元素都小 → it == m.begin()
if(it == m.begin())
{
cout << it->first << "\n";
m.erase(it->first);
break;
}
// 情况4:l 在中间,有左右邻居
auto it2 = prev(it); // 左邻居(< l)
ll left_val = it2->first;
ll right_val = it->first;
ll dist_left = l - left_val;
ll dist_right = right_val - l;
if(dist_left < dist_right)
{
cout << left_val << "\n";
m.erase(left_val);
}
else if(dist_left > dist_right)
{
cout << right_val << "\n";
m.erase(right_val);
}
else // 距离相等,选较短的(即 left_val)
{
cout << left_val << "\n";
m.erase(left_val);
}
break;
}
}
return 0;
}
4.5 小梦的AB交换
- 见1.1.2
4.6 求区间和
!洛谷
B3612 【深进1.例1】求区间和
题目描述
给定 n n n 个正整数组成的数列 a 1 , a 2 , ⋯ , a n a_1, a_2, \cdots, a_n a1,a2,⋯,an 和 m m m 个区间 [ l i , r i ] [l_i,r_i] [li,ri],分别求这 m m m 个区间的区间和。
输入格式
第一行包含一个正整数 n n n,表示序列的长度。
第二行包含 n n n 个正整数 a 1 , a 2 , ⋯ , a n a_1,a_2, \cdots ,a_n a1,a2,⋯,an。
第三行包含一个正整数 m m m,表示区间的数量。
接下来 m m m 行,每行包含两个正整数 l i , r i l_i,r_i li,ri,满足 1 ≤ l i ≤ r i ≤ n 1\le l_i\le r_i\le n 1≤li≤ri≤n。
输出格式
共 m m m 行,其中第 i i i 行包含一个正整数,表示第 i i i 组答案的询问。
输入输出样例 #1
输入 #1
4 4 3 2 1 2 1 4 2 3输出 #1
10 5说明/提示
样例解释
第 1 1 1 到第 4 4 4 个数加起来和为 10 10 10。第 2 2 2 个数到第 3 3 3 个数加起来和为 5 5 5。
数据范围
对于 50 % 50 \% 50% 的数据: n , m ≤ 1000 n,m\le 1000 n,m≤1000;
对于 100 % 100 \% 100% 的数据: 1 ≤ n , m ≤ 10 5 1 \le n, m\le 10^5 1≤n,m≤105, 1 ≤ a i ≤ 10 4 1 \le a_i\le 10^4 1≤ai≤104。
思路
简单前缀和
代码:
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1e5+10;
ll n;
ll a[N];
ll m;
ll l,r;
ll b[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n;
for(int i = 1; i<= n; i++)cin >> a[i];
for(int i = 1; i<= n; i++)b[i] = b[i-1]+a[i];
cin >> m;
while(m--)
{
cin >> l >> r;
cout << b[r]-b[l-1] << "\n";
}
return 0;
}
4.7 数列前缀和(二维)
!洛谷
B3693 数列前缀和 4
题目背景
这次不是数列的问题了。
题目描述
给定一个 n n n 行 m m m 列的矩阵 a a a,有 q q q 次询问,每次给定 ( u , v ) (u, v) (u,v) 和 ( x , y ) (x, y) (x,y),请你求出:
( ∑ i = u x ∑ j = v y a i , j ) m o d 2 64 (\sum_{i = u}^x \sum_{j = v}^y a_{i,j}) \bmod 2^{64} (i=u∑xj=v∑yai,j)mod264
也就是求出以 ( u , v ) (u, v) (u,v) 为左上角、 ( x , y ) (x,y) (x,y) 为右下角的矩形元素和对 2 64 2^{64} 264 取余数的结果。
输入格式
本题单测试点内有多组测试数据。
输入的第一行是一个整数 T T T,表示数据组数。接下来依次给出每组数据的输入信息:
第一行三个整数,依次表示矩阵的行数 n n n 和列数 m m m 以及询问数 q q q。
接下来 n n n 行,每行 m m m 个整数。第 i i i 行第 j j j 个整数表示 a i , j a_{i,j} ai,j。
接下来 q q q 行,每行四个整数,依次为 u , v , x , y u,v,x, y u,v,x,y,表示一组询问。
输出格式
为了避免输出过大,对于每组数据,请输出一行一个整数,表示本组数据的所有询问的答案的按位异或和。
输入输出样例 #1
输入 #1
2 3 3 3 1 2 3 4 5 6 7 8 9 1 1 3 3 2 1 2 2 1 2 2 3 2 2 1 1 3 4 6 2 2 2 2输出 #1
52 6说明/提示
样例 1 解释
对第一组数据,三次询问的答案依次为 45 , 9 , 16 45,9,16 45,9,16。其按位异或和为 52 52 52。
数据规模与约定
对全部的测试点,保证 1 ≤ T ≤ 10 1 \leq T \leq 10 1≤T≤10, 1 ≤ n , m ≤ 10 3 1 \leq n, m \leq 10^3 1≤n,m≤103, 1 ≤ q ≤ 10 6 1 \leq q \leq 10^6 1≤q≤106, 0 ≤ a i < 2 64 0 \leq a_i < 2^{64} 0≤ai<264, 1 ≤ u ≤ x ≤ n 1 \leq u \leq x \leq n 1≤u≤x≤n, 1 ≤ v ≤ y ≤ m 1 \leq v \leq y \leq m 1≤v≤y≤m。
数据保证 ∑ ( n × m ) ≤ 10 6 \sum(n \times m) \leq 10^6 ∑(n×m)≤106, ∑ q ≤ 10 6 \sum q \leq 10^6 ∑q≤106。即输入矩阵的总大小和询问总数均不超过 10 6 10^6 106。
提示
如果你不知道什么是按位异或和,可以在你的代码里添加如下的函数:
cpptemplate <class T> T getXorSum(T *begin, T *end) { T ret = 0; for (T *it = begin; it != end; ++it) ret ^= *it; return ret; }这一函数的作用是计算传入数组(包括
std::vector)某一左闭右开区间的按位异或和,返回值类型与传入数组的类型相同,调用方法与std::sort类似,例如,要求数组 a a a 的 a 1 ∼ a n a_1 \sim a_n a1∼an 的按位异或和,则调用getXorSum(a + 1, a + 1 + n),求 a 0 ∼ a n − 1 a_0 \sim a_{n - 1} a0∼an−1 的按位异或和,则调用getXorSum(a, a + n)。如果 a a a 是std::vector,则将上述调用代码里的a均改为a.begin()即可。
问题
- 数据范围超过 2 64 2^{64} 264 要用 unsigned long long
- C++ 中的 unsigned long long 类型在进行加减乘运算时,如果结果超过 2 64 2^{64} 264 会自动发生自然溢出(相当于自动对取模)。因此,不需要手动写 % 操作,直接使用 unsigned long long 存储即可
- 二维前缀和
- 构造 :
b[i][j] = b[i-1][j] + b[i][j-1] - b[i-1][j-1] + a[i][j]; - 计算 :
b[x][y] - b[u-1][y] - b[x][v-1] + b[u-1][v-1];
- 构造 :
思路:
二维前缀和
代码:
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e3 + 10;
ll T;
ll n, m, q;
unsigned long long a[N][N];
unsigned long long b[N][N];
ll u, v, x, y;
unsigned long long ret;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> T;
while(T--)
{
cin >> n >> m >> q;
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
cin >> a[i][j];//读入矩阵
b[i][j] = b[i-1][j] + b[i][j-1] - b[i-1][j-1] + a[i][j];//构造二维前缀和数组
}
}
ret = 0;
while(q--)
{
cin >> u >> v >> x >> y;
ret ^= b[x][y] - b[u-1][y] - b[x][v-1] + b[u-1][v-1];
}
cout << ret << "\n";
}
return 0;
}
问题
m.count()只返回0/1;即元素是否存在
代码:
- map写法
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
int n, k, q;
int l[N], r[N];
int a, b;
int i;
map<int,int> m;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> k >> q;
while(n--)
{
i++;
cin >> l[i] >> r[i];
for(int j = l[i]; j <= r[i]; j++)
{
m[j]++;
}
}
while(q--)
{
cin >> a >> b;
int ret = 0;
for(int j = a; j <= b; j++)
{
int n um = m[j];
if(num >= k)
{
ret++;
}
}
cout << ret << "\n";
}
return 0;
}
- 差分/前缀和写法
cpp
#include<stdio.h>
using namespace std;
int f[200005];
int main(){
int n,m,k;
scanf("%d%d%d",&n,&m,&k);
int i;
int max=0;
for(i=1;i<=n;i++)
{//差分代码
int a,b;
scanf("%d%d",&a,&b);// 读取推荐温度区间 [a, b]
f[a]+=1;// 【差分标记】在起点 a 处 +1
f[b+1]-=1;// 【差分标记】在终点后一位 b+1 处 -1
if(b>max)max=b; // 记录最大的右边界,优化后续循环
}
for(i=1;i<=max;i++)
{ //用求前缀和的方法还原差分数组
f[i]=f[i-1]+f[i];
}
for(i=1;i<=200000;i++)
{//预处理(前缀和) 如果当前温度 i 的推荐数 >= m (即题目中的k),则标记为1,否则为0
if(f[i]>=m)f[i]=1+f[i-1];// 是有效温度:在前一个前缀和的基础上 +1
else f[i]=f[i-1];// 是无效温度:继承前一个前缀和的值
}
for(i=1;i<=k;i++)
{
int l,r;
scanf("%d%d",&l,&r);
printf("%d\n",f[r]-f[l-1]);//前缀和计算
}
return 0;
}
4.8 k 倍区间
!洛谷
P8649 [蓝桥杯 2017 省 B] k 倍区间
P8649 [蓝桥杯 2017 省 B] k 倍区间 - 洛谷
题目描述
给定一个长度为 N N N 的数列, A 1 , A 2 , ⋯ A N A_1,A_2, \cdots A_N A1,A2,⋯AN,如果其中一段连续的子序列 A i , A i + 1 , ⋯ A j ( i ≤ j ) A_i,A_{i+1}, \cdots A_j(i \le j) Ai,Ai+1,⋯Aj(i≤j) 之和是 K K K 的倍数,我们就称这个区间 [ i , j ] [i,j] [i,j] 是 K K K 倍区间。
你能求出数列中总共有多少个 K K K 倍区间吗?
输入格式
第一行包含两个整数 N N N 和 K K K ( 1 ≤ N , K ≤ 10 5 ) (1 \le N,K \le 10^5) (1≤N,K≤105)。
以下 N N N 行每行包含一个整数 A i A_i Ai ( 1 ≤ A i ≤ 10 5 ) (1 \le A_i \le 10^5) (1≤Ai≤105)。
输出格式
输出一个整数,代表 K K K 倍区间的数目。
输入输出样例 #1
输入 #1
5 2 1 2 3 4 5输出 #1
6说明/提示
时限 2 秒, 256M。蓝桥杯 2017 年第八届
思路:
同余定理:
假设我们有一个数组的前缀和数组 b b b。
如果两个前缀和 b [ i ] b[i] b[i] 和 b [ j ] b[j] b[j] 对 k k k 取模的结果相同(即 b [ i ] ≡ b [ j ] ( m o d k ) b[i] \equiv b[j] \pmod k b[i]≡b[j](modk)),那么它们之间的差值一定是 k k k 的倍数。
代码:
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
ll n, k;
ll a[N];
ll b[N];
ll c[N];
ll ret;
ll num;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> k;
for(int i= 1; i<= n; i++)
{
cin >> a[i];//读入数组
b[i] = b[i-1] + a[i];//构造前缀和数组
}
for(int i = 0; i<= n; i++)
{
ret += c[b[i]%k]++;//核心
}
cout << ret;
return 0;
}
5. 蓝桥杯真题
5.1 蓝桥杯 2025 省赛 C++ B组(第二场)
5.1.1 密密摆放
!洛谷
P12337 [蓝桥杯 2025 省 AB/Python B 第二场] 密密摆放
P12337 [蓝桥杯 2025 省 AB/Python B 第二场] 密密摆放 - 洛谷
题目描述
小蓝有一个大箱子,内部的长宽高分别是 200 200 200、 250 250 250、 240 240 240(单位:毫米),他要用这个大箱子来放一些同样大小的小盒子,小盒子的外部长宽高分别是 30 30 30、 40 40 40、 50 50 50(单位:毫米)。小盒子允许从各个方向旋转(包括可以平放和倒放)。
请问小蓝最多可以在一个大箱子里面放多少个小盒子。
输入格式
无
输出格式
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只需要编写一个程序输出这个整数,输出多余的内容将无法得分。
代码:
cpp
#include<iostream>
using namespace std;
int main()
{
cout << 8*5*5 ;
return 0;
}
5.1.2 脉冲强度之和
!洛谷
P12338 [蓝桥杯 2025 省 B/Python B 第二场] 脉冲强度之和
P12338 [蓝桥杯 2025 省 B/Python B 第二场] 脉冲强度之和 - 洛谷
题目描述
在蓝桥电子工坊,工程师小蓝正在设计一款智能脉冲生成器,用于驱动一种新型设备。该设备的运行依赖于特定的脉冲强度,用正整数 p p p 表示,其必须满足以下三个条件:
- 可由连续 10 10 10 个正整数之和组成:即存在一个正整数 k k k,使得脉冲强度 p = k + ( k + 1 ) + ( k + 2 ) + ⋯ + ( k + 9 ) p = k + (k + 1) + (k + 2) + \cdots + (k + 9) p=k+(k+1)+(k+2)+⋯+(k+9)。
- 各个数位上的数字都相同:例如 1111 1111 1111、 22222 22222 22222、 333333 333333 333333 等。
- 数值不超过 20255202 20255202 20255202:即 1 ≤ p ≤ 20255202 1 \leq p \leq 20255202 1≤p≤20255202。
通过计算所有符合条件的脉冲强度之和,小蓝能够优化设备运行模式。对此,请帮助他计算这一总和。
输入格式
无
输出格式
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只需要编写一个程序输出这个整数,输出多余的内容将无法得分。
思路:
- 暴力枚举
代码:
cpp
#include<bits/stdc++.h>
using namespace std;
// typedef unsigned long long ll;
// ll p, k;
// ll ret;
// int isright(ll x)
// {
// if(x-10 < 0)return 1;
// string s = to_string(x);
// for(int i = 1; i< s.size(); i++)
// {
// if(s[i] != s[0])return 0;
// }
// cout << "p = " << x << endl;
// return x;
// }
int main()
{
// for(int i = 1; p < 20255202; i++)
// {
// p = 0;
// for(int k = i; k <= i+9; k++)
// {
// p += k;
// }
// ret += isright(p);
// if(isright(p))cout << "i=" << i << "\n";
// }
// cout << "符合条件的脉冲强度之和=" << ret;
cout << "6172830";
return 0;
}
5.1.3 25 之和
!洛谷
P12339 [蓝桥杯 2025 省 B/Python B 第二场] 25 之和
P12339 [蓝桥杯 2025 省 B/Python B 第二场] 25 之和 - 洛谷
题目描述
小蓝最近对求和很着迷,给定一个正整数 n n n,他想求从 n n n 开始的连续 25 25 25 个整数的和,即 n + ( n + 1 ) + ( n + 2 ) + ⋯ + ( n + 24 ) n + (n + 1) + (n + 2) + \cdots + (n + 24) n+(n+1)+(n+2)+⋯+(n+24),请帮帮他吧。
输入格式
输入一行包含一个正整数 n n n。
输出格式
输出一行包含一个整数表示答案。
输入输出样例 #1
输入 #1
1输出 #1
325输入输出样例 #2
输入 #2
100输出 #2
2800说明/提示
评测用例规模与约定
- 对于 40 % 40\% 40% 的评测用例, 1 ≤ n ≤ 100 1 \leq n \leq 100 1≤n≤100;
- 对于所有评测用例, 1 ≤ n ≤ 10000 1 \leq n \leq 10000 1≤n≤10000。
代码:
cpp
#include<iostream>
using namespace std;
typedef unsigned long long ll;
ll n;
ll sum;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n;
cout << 25*n+(1+24)*24/2;
return 0;
}
5.1.4 旗帜
!洛谷
P12340 [蓝桥杯 2025 省 AB/Python B 第二场] 旗帜
P12340 [蓝桥杯 2025 省 AB/Python B 第二场] 旗帜 - 洛谷
题目描述
小蓝要画一个 LANQIAO 图形,并把这个图形做成一个旗帜。图形的形状为一个 h × w h \times w h×w 的矩形,其中 h h h 表示图形的高, w w w 表示图形的宽。当 h = 5 , w = 10 h = 5, w = 10 h=5,w=10 时,图形如下所示:
LANQIAOLAN ANQIAOLANQ NQIAOLANQI QIAOLANQIA IAOLANQIAO图形的规律是:第一行用
LANQIAO重复填入,第二行开始,每行向左移动一个字符,用LANQIAO重复填入。小蓝需要把图形中的每个字母都剪出来,以粘贴到旗帜上,他想知道,给定图形的高和宽,图形中有多少个
A。输入格式
输入的第一行包含两个正整数 h , w h, w h,w,用一个空格分隔。
输出格式
输出一行包含一个整数表示答案。
输入输出样例 #1
输入 #1
5 10输出 #1
14说明/提示
评测用例规模与约定
- 对于 30 % 30\% 30% 的评测用例, h = 1 h = 1 h=1, 1 ≤ w ≤ 20 1 \leq w \leq 20 1≤w≤20;
- 对于 60 % 60\% 60% 的评测用例, 1 ≤ h , w ≤ 20 1 \leq h, w \leq 20 1≤h,w≤20;
- 对于所有评测用例, 1 ≤ h , w ≤ 100 1 \leq h, w \leq 100 1≤h,w≤100。
思路:
模拟
代码:
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[8][8] ={{6, 7, 1, 2, 3, 4, 5, 0},
{7, 1, 2, 3, 4, 5, 6, 0},
{1, 2, 3, 4, 5, 6, 7, 0},
{2, 3, 4, 5, 6, 7, 1, 0},
{3, 4, 5, 6, 7, 1, 2, 0},
{4, 5, 6, 7, 1, 2, 3, 0},
{5, 6, 7, 1, 2, 3, 4, 0},
{0, 0, 0, 0, 0, 0, 0, 0}};
ll h, w;
ll ret;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> h >> w;
for(int i= 1; i <= h; i++)
{
// cout << "i=" << i << " ";
for(int j = 1; j <= w; j++)
{
// cout << "j=" << j << endl;
if(a[i%7][j%7] == 2||a[i%7][j%7] == 6)
{
// cout << "a[i%7][j%7]=" << a[i%7][j%7] << endl;
ret++;
}
}
}
cout << ret;
return 0;
}
5.1.5 数列差分
!洛谷
P12342 [蓝桥杯 2025 省 B/Python B 第二场] 数列差分
P12342 [蓝桥杯 2025 省 B/Python B 第二场] 数列差分 - 洛谷
题目描述
小蓝有两个长度均为 n n n 的数列 A = { a 1 , a 2 , ⋯ , a n } A=\{a_1, a_2, \cdots, a_n\} A={a1,a2,⋯,an} 和 B = { b 1 , b 2 , ⋯ , b n } B=\{b_1, b_2, \cdots, b_n\} B={b1,b2,⋯,bn},将两个数列作差定义为 C = A − B = { c 1 = a 1 − b 1 , c 2 = a 2 − b 2 , ⋯ , c n = a n − b n } C=A-B=\{c_1=a_1-b_1, c_2=a_2-b_2, \cdots, c_n=a_n-b_n\} C=A−B={c1=a1−b1,c2=a2−b2,⋯,cn=an−bn}。小蓝将对数列 B B B 进行若干次操作,每次操作可以将数列 B B B 中的任意一个数更改为任意一个整数。在进行完所有操作后,小蓝可以按任意顺序将数列 B B B 重排,之后再计算数列 C C C。小蓝想知道,最少操作多少次可以使得数列 C C C 中的所有数都为正整数。
输入格式
输入的第一行包含一个正整数 n n n;
第二行包含 n n n 个整数 a 1 , a 2 , ⋯ , a n a_1, a_2, \cdots, a_n a1,a2,⋯,an,相邻整数之间使用一个空格分隔。
第三行包含 n n n 个整数 b 1 , b 2 , ⋯ , b n b_1, b_2, \cdots, b_n b1,b2,⋯,bn,相邻整数之间使用一个空格分隔。
输出格式
输出一行包含一个整数表示答案。
输入输出样例 #1
输入 #1
4 22 31 12 14 3 19 27 44输出 #1
1说明/提示
样例说明
其中一种方案:将 44 44 44 改为 0 0 0,重新排列 B B B 为 { 19 , 27 , 3 , 0 } \{19, 27, 3, 0\} {19,27,3,0},使得数列 C = { 3 , 4 , 9 , 14 } C=\{3, 4, 9, 14\} C={3,4,9,14} 均为正整数。
评测用例规模与约定
- 对于 30 % 30\% 30% 的评测用例, n ≤ 10 n \leq 10 n≤10;
- 对于所有评测用例, 1 ≤ n ≤ 10 5 1 \leq n \leq 10^5 1≤n≤105, − 10 9 ≤ a i ≤ 10 9 -10^9 \leq a_i \leq 10^9 −109≤ai≤109, − 10 9 ≤ b i ≤ 10 9 -10^9 \leq b_i \leq 10^9 −109≤bi≤109。
思路
贪心
感觉思路其实有问题,但是样例全过了
代码:
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 +10;
ll a[N];
ll b[N];
ll n;
ll ret;
ll k;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n;
for(int i = 1; i<= n; i++) cin >> a[i];
for(int i = 1; i<= n; i++) cin >> b[i];
sort(a+1,a+n+1);
sort(b+1,b+n+1);
for(int i = 1; i<= n; i++)
{
if(b[i-k] >= a[i])
{
ret++;
k++;
}
}
cout << ret;
return 0;
}
- 正常 双指针+贪心做法
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 +10;
ll a[N], b[N];
int n;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
for(int i = 1; i <= n; i++) cin >> b[i];
sort(a+1, a+n+1);
sort(b+1, b+n+1);
int j = 1; // b 的指针
int success = 0; // 成功匹配的对数
// 贪心:尝试为每一个 a[i] 找一个最小的、且小于它的 b[j]
for(int i = 1; i <= n; i++)
{
// 如果当前的 b 比 a 小,说明匹配成功
if(j <= n && b[j] < a[i])
{
success++;
j++; // 这个 b 用过了,看下一个 b
}
// 如果 b[j] >= a[i],说明 b 太大了,这个 a[i] 没法匹配当前的 b
// 但 a[i] 也没法匹配后面更大的 b,所以这个 a[i] 只能放弃(或者说对应的 b 需要被修改)
// 实际上,我们是在统计有多少个 b 能留下来。
}
// 答案 = 总数 - 能留下的 b 的数量
cout << n - success << endl;
return 0;
}

